From d2376c9713445a98cee9a24cfeb1029d140d19f7 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Wed, 4 Sep 2024 19:35:47 +0200 Subject: included different cpp coding styles and a latex standford style --- contributing.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contributing.md b/contributing.md index 40e1edd..57a55b7 100644 --- a/contributing.md +++ b/contributing.md @@ -14,6 +14,7 @@ - TODO: tagging / versions + # Code style - Formatting nitty-gritty is handled by clang-format/clang-tidy @@ -21,6 +22,9 @@ - When using libraries of which the header include order is important, make sure to separate the include statements using a blank line (clang-format may sort include statements, but does not sort across empty lines). +- [Cpp practices](https://lefticus.gitbooks.io/cpp-best-practices/content/) +- [C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) +- [Google c++ style](https://google.github.io/styleguide/cppguide.html) ## CMakeLists specific @@ -29,9 +33,15 @@ resolving merge conflicts when multiple sources were added by different people to the same CMakeLists.txt easier. +## Doxygen style + +- [a C-style doxygen example](https://www.cs.cmu.edu/~410/doc/doxygen.html) + + # Documentation - All documentation is written in U.S. English +- [stanford latex style](https://web.stanford.edu/class/ee364b/latex_templates/template_notes.pdf) - TODO # Libraries -- cgit v1.2.3 From de77372027053744604cba732dce318159289b76 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Fri, 6 Sep 2024 09:41:13 +0200 Subject: style code example added and added more examples in contributing.md --- code-style-example/style.cpp | 15 ++++++++++++ code-style-example/style.h | 58 ++++++++++++++++++++++++++++++++++++++++++++ contributing.md | 33 +++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 code-style-example/style.cpp create mode 100644 code-style-example/style.h diff --git a/code-style-example/style.cpp b/code-style-example/style.cpp new file mode 100644 index 0000000..a42fca8 --- /dev/null +++ b/code-style-example/style.cpp @@ -0,0 +1,15 @@ + + +#include "style.h" + +MyClass::MyClass(int t_value) : m_value(t_value) {} + +MyClass::MyClass() { m_value = 0; } + +MyClass::~MyClass() {} + +const int MyClass::get_value() const { return m_value; } + +void MyClass::set_value(const int t_value) { m_value = t_value; } + +void MyClass::increment() { m_value += 1; } diff --git a/code-style-example/style.h b/code-style-example/style.h new file mode 100644 index 0000000..2a4022b --- /dev/null +++ b/code-style-example/style.h @@ -0,0 +1,58 @@ +/*! @file MyClass.h */ + +#ifndef MYPROJECT_MYCLASS_HPP +#define MYPROJECT_MYCLASS_HPP + +/** + * @brief example class + */ +class MyClass { + +public: + /** + * @brief example constructor + * + * @param[integer] t_value example first argument + */ + MyClass(int t_value); + + /** + * @brief constructor example + * + */ + MyClass(); + + /** + * @brief deconstuctor example + * + */ + ~MyClass(); + + /** + * @brief setter example with correct const implementation + * + * @param[const integer] t_value first argument + */ + void set_value(const int t_value); + + /** + * @brief getter example with correct const implementation + * + * @return const integer + */ + const int get_value() const; + + /** + * @brief increment member m_value + * + */ + void increment(); + +private: + /** + * @m_value basic member example + */ + int m_value; +}; + +#endif diff --git a/contributing.md b/contributing.md index 57a55b7..2d8a8cf 100644 --- a/contributing.md +++ b/contributing.md @@ -26,6 +26,39 @@ - [C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) - [Google c++ style](https://google.github.io/styleguide/cppguide.html) +```cpp +code-style-example/style.h +``` + +```cpp +code-style-example/style.cpp +``` + +```cpp +// good +class MyClass +{ +public: + void do_something(const int i); + void do_something(const std::string &str); +}; +``` + +```cpp +// instead of doing this +auto s = "Hello"; +auto x = "42"s; +auto x = 42; +auto x = 42.f; + +// Do this +std::string s = "Hello"; +std::string x = "42"s; +int x = 42; +float x = 42.f; +``` + + ## CMakeLists specific - Make sure list arguments (e.g. sources, libraries) given to commands (e.g. -- cgit v1.2.3 From 25eb01beedce355ddf50052cca4938294100d9ed Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Fri, 6 Sep 2024 10:08:20 +0200 Subject: updated the contributing.md --- contributing.md | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) diff --git a/contributing.md b/contributing.md index 2d8a8cf..a917166 100644 --- a/contributing.md +++ b/contributing.md @@ -26,14 +26,90 @@ - [C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) - [Google c++ style](https://google.github.io/styleguide/cppguide.html) + +- .h basic code style with doxygen ```cpp -code-style-example/style.h +// code-style-example/style.cpp +/*! @file MyClass.h */ + +#ifndef MYPROJECT_MYCLASS_HPP +#define MYPROJECT_MYCLASS_HPP + +/** + * @brief example class + */ +class MyClass { + +public: + /** + * @brief example constructor + * + * @param[integer] t_value example first argument + */ + MyClass(int t_value); + + /** + * @brief constructor example + * + */ + MyClass(); + + /** + * @brief deconstuctor example + * + */ + ~MyClass(); + + /** + * @brief setter example with correct const implementation + * + * @param[const integer] t_value first argument + */ + void set_value(const int t_value); + + /** + * @brief getter example with correct const implementation + * + * @return const integer + */ + const int get_value() const; + + /** + * @brief increment member m_value + * + */ + void increment(); + +private: + /** + * @m_value basic member example + */ + int m_value; +}; + +#endif ``` +- .cpp basic code style ```cpp -code-style-example/style.cpp +// code-style-example/style.cpp + +#include "style.h" + +MyClass::MyClass(int t_value) : m_value(t_value) {} + +MyClass::MyClass() { m_value = 0; } + +MyClass::~MyClass() {} + +const int MyClass::get_value() const { return m_value; } + +void MyClass::set_value(const int t_value) { m_value = t_value; } + +void MyClass::increment() { m_value += 1; } ``` +- when to use references ```cpp // good class MyClass @@ -43,6 +119,7 @@ public: void do_something(const std::string &str); }; ``` +- Do not use auto ```cpp // instead of doing this @@ -57,7 +134,53 @@ std::string x = "42"s; int x = 42; float x = 42.f; ``` +- Do not define constants as define but instead directly declare it. +```cpp +// Bad Idea +#define PI 3.14159; + +// Good Idea +namespace my_project { + class Constants { + public: + // if the above macro would be expanded, then the following line would be: + // static const double 3.14159 = 3.14159; + // which leads to a compile-time error. Sometimes such errors are hard to understand. + static constexpr double PI = 3.14159; + }; +} +``` +- Use enum class instead of using enum only to prevent bugs +```cpp +void Print_color(int color); + +enum Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF }; +enum Product_info { red = 0, purple = 1, blue = 2 }; + +Web_color webby = Web_color::blue; + +// Clearly at least one of these calls is buggy. +Print_color(webby); +Print_color(Product_info::blue); +``` +Instead use an enum class: +```cpp + +void Print_color(int color); + +enum class Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF }; +enum class Product_info { red = 0, purple = 1, blue = 2 }; + +Web_color webby = Web_color::blue; +Print_color(webby); // Error: cannot convert Web_color to int. +Print_color(Product_info::red); // Error: cannot convert Product_info to int. +``` +- Think about using size_t instead of using int for preventing processor mismatching. This might prevent a bug in the future. +```cpp +std::size_t i = 0; // use this instead of using a int +double x = 0.0; +``` ## CMakeLists specific -- cgit v1.2.3 From f3cbb5e1176048969e778581d2e935604e27daac Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Fri, 6 Sep 2024 10:19:36 +0200 Subject: added another example to contributing.md --- contributing.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/contributing.md b/contributing.md index a917166..cc22cd5 100644 --- a/contributing.md +++ b/contributing.md @@ -181,6 +181,18 @@ Print_color(Product_info::red); // Error: cannot convert Product_info to int. std::size_t i = 0; // use this instead of using a int double x = 0.0; ``` +- comment style guide, you can use TODO:, FIXME:, NOTE:, or HACK: as admonitions when needed. + +```cpp +// Compute the first 10,000 decimals of Pi. +// FIXME: Don't crash when computing the 1,337th decimal due to `increment` +// being negative. +``` +bad example +```cpp +// Draw loading screen. +draw_load_screen(); +``` ## CMakeLists specific -- cgit v1.2.3 From 666a77004bbd8909b24355ef316b099f0683570f Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Mon, 9 Sep 2024 15:00:03 +0200 Subject: adjusted contributing after reviewed by loek --- contributing.md | 80 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/contributing.md b/contributing.md index cc22cd5..0c24560 100644 --- a/contributing.md +++ b/contributing.md @@ -110,42 +110,78 @@ void MyClass::increment() { m_value += 1; } ``` - when to use references + +-- If the input parameter in the function cannot accept a nullptr ```cpp -// good -class MyClass -{ -public: - void do_something(const int i); - void do_something(const std::string &str); -}; +void foo::function(const std::string& name){}; +``` +-- Use a reference when you know that the object will exist for the lifetime of the reference and when nullability is not a concern. +```cpp +int x = 10; +int& ref = x; +ref = 20; +``` + +-- If a function should return a reference to an object that exists elsewhere (such as a member of a class or an element in a container), you can return a reference. +```cpp +container& get_element(std::vector& vec, size_t index){ + return vec[index]; +} +``` +- When to use pointers + +-- Use pointers when dealing with dynamic memory +```cpp +int* ptr = new int(5); +delete ptr; +``` + +-- Pointers can be nullptr, allowing them to represent "no object." Use pointers when you need to express optional ownership or reference. +```cpp +void foo::function(int* ptr){ + if(ptr){ + // ptr is not null + } +} +``` + +-- When dealing with polymorphism. +```cpp +class Base {}; +class Derived : public Base {}; + +Base* obj = new Derived(); +obj->function(); ``` + + - Do not use auto +-- Instead of doing this ```cpp -// instead of doing this auto s = "Hello"; auto x = "42"s; auto x = 42; auto x = 42.f; - -// Do this +``` +-- Do this. +```cpp std::string s = "Hello"; std::string x = "42"s; int x = 42; float x = 42.f; ``` - Do not define constants as define but instead directly declare it. +-- This is wrong. ```cpp -// Bad Idea #define PI 3.14159; +``` -// Good Idea +-- Do this. +```cpp namespace my_project { class Constants { public: - // if the above macro would be expanded, then the following line would be: - // static const double 3.14159 = 3.14159; - // which leads to a compile-time error. Sometimes such errors are hard to understand. static constexpr double PI = 3.14159; }; } @@ -163,7 +199,7 @@ Web_color webby = Web_color::blue; Print_color(webby); Print_color(Product_info::blue); ``` -Instead use an enum class: +- Instead use an enum class: ```cpp void Print_color(int color); @@ -176,6 +212,7 @@ Print_color(webby); // Error: cannot convert Web_color to int. Print_color(Product_info::red); // Error: cannot convert Product_info to int. ``` + - Think about using size_t instead of using int for preventing processor mismatching. This might prevent a bug in the future. ```cpp std::size_t i = 0; // use this instead of using a int @@ -183,16 +220,17 @@ double x = 0.0; ``` - comment style guide, you can use TODO:, FIXME:, NOTE:, or HACK: as admonitions when needed. +-- Bad example +```cpp +// Draw loading screen. +draw_load_screen(); +``` +-- Better ```cpp // Compute the first 10,000 decimals of Pi. // FIXME: Don't crash when computing the 1,337th decimal due to `increment` // being negative. ``` -bad example -```cpp -// Draw loading screen. -draw_load_screen(); -``` ## CMakeLists specific -- cgit v1.2.3 From 51f7699966ab856c873648ee25621b80421f8c8f Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Mon, 16 Sep 2024 17:34:05 +0200 Subject: loading images work through resource manager, however needs return fixing. cause right now static cast needs to be done outside --- resource-manager/Audio_asset.cpp | 0 resource-manager/Audio_asset.h | 12 +++++++ resource-manager/CMakeLists.txt | 34 ++++++++++++++++++ resource-manager/Image_asset.cpp | 12 +++++++ resource-manager/Image_asset.h | 19 +++++++++++ resource-manager/Untitled.jpeg | Bin 0 -> 6756 bytes resource-manager/constants.cpp | 10 ++++++ resource-manager/constants.h | 16 +++++++++ resource-manager/img.png | Bin 0 -> 92742 bytes resource-manager/main.cpp | 57 +++++++++++++++++++++++++++++++ resource-manager/resource.h | 11 ++++++ resource-manager/resource_fabricator.cpp | 30 ++++++++++++++++ resource-manager/resource_fabricator.h | 23 +++++++++++++ resource-manager/resource_manager.cpp | 48 ++++++++++++++++++++++++++ resource-manager/resource_manager.h | 32 +++++++++++++++++ 15 files changed, 304 insertions(+) create mode 100644 resource-manager/Audio_asset.cpp create mode 100644 resource-manager/Audio_asset.h create mode 100644 resource-manager/CMakeLists.txt create mode 100644 resource-manager/Image_asset.cpp create mode 100644 resource-manager/Image_asset.h create mode 100644 resource-manager/Untitled.jpeg create mode 100644 resource-manager/constants.cpp create mode 100644 resource-manager/constants.h create mode 100644 resource-manager/img.png create mode 100644 resource-manager/main.cpp create mode 100644 resource-manager/resource.h create mode 100644 resource-manager/resource_fabricator.cpp create mode 100644 resource-manager/resource_fabricator.h create mode 100644 resource-manager/resource_manager.cpp create mode 100644 resource-manager/resource_manager.h diff --git a/resource-manager/Audio_asset.cpp b/resource-manager/Audio_asset.cpp new file mode 100644 index 0000000..e69de29 diff --git a/resource-manager/Audio_asset.h b/resource-manager/Audio_asset.h new file mode 100644 index 0000000..87fa762 --- /dev/null +++ b/resource-manager/Audio_asset.h @@ -0,0 +1,12 @@ +#pragma once + + +#include "resource.h" + + +class Audio : public Resource { + + + + +}; diff --git a/resource-manager/CMakeLists.txt b/resource-manager/CMakeLists.txt new file mode 100644 index 0000000..5c2c962 --- /dev/null +++ b/resource-manager/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.28) + +# Set C and C++ standards +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) + +# Enable debug features +set(CMAKE_BUILD_TYPE Debug) +add_compile_definitions(DEBUG) + +# Project name and supported languages +project(crepe C CXX) + +# Find SDL2 +find_package(SDL2 REQUIRED) +find_package(SDL2_image REQUIRED) + +# Add all source files to the executable +add_executable(main + main.cpp + Audio_asset.cpp + Image_asset.cpp + resource_fabricator.cpp + resource_manager.cpp + constants.cpp +) + +# Include directories for headers +target_include_directories(main PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_IMAGE_INCLUDE_DIRS} .) + +# Link SDL2 library +target_link_libraries(main PRIVATE ${SDL2_LIBRARIES} SDL2_image::SDL2_image) + diff --git a/resource-manager/Image_asset.cpp b/resource-manager/Image_asset.cpp new file mode 100644 index 0000000..77eee2f --- /dev/null +++ b/resource-manager/Image_asset.cpp @@ -0,0 +1,12 @@ + + +#include "Image_asset.h" +#include + + + +Image::~Image(){ + if (surface) { + SDL_FreeSurface(surface); + } +} diff --git a/resource-manager/Image_asset.h b/resource-manager/Image_asset.h new file mode 100644 index 0000000..e7d13e9 --- /dev/null +++ b/resource-manager/Image_asset.h @@ -0,0 +1,19 @@ +#pragma once + + + +#include "resource.h" +#include + + + + + +class Image : public Resource { + +public: + SDL_Surface* surface = nullptr; + + ~Image(); + +}; diff --git a/resource-manager/Untitled.jpeg b/resource-manager/Untitled.jpeg new file mode 100644 index 0000000..aa4d289 Binary files /dev/null and b/resource-manager/Untitled.jpeg differ diff --git a/resource-manager/constants.cpp b/resource-manager/constants.cpp new file mode 100644 index 0000000..f9fbf5b --- /dev/null +++ b/resource-manager/constants.cpp @@ -0,0 +1,10 @@ +#include "constants.h" +#include + + + +namespace crepe { + + const std::string Constants::PNG_EXT = ".png"; + const std::string Constants::OGG_EXT = ".ogg"; +} diff --git a/resource-manager/constants.h b/resource-manager/constants.h new file mode 100644 index 0000000..620ddf3 --- /dev/null +++ b/resource-manager/constants.h @@ -0,0 +1,16 @@ + +#pragma once + +#include +namespace crepe { + + class Constants { + public: + using FILE_PATH = std::string; + + + static const std::string PNG_EXT; + static const std::string OGG_EXT; + + }; +} diff --git a/resource-manager/img.png b/resource-manager/img.png new file mode 100644 index 0000000..43b1eca Binary files /dev/null and b/resource-manager/img.png differ diff --git a/resource-manager/main.cpp b/resource-manager/main.cpp new file mode 100644 index 0000000..62426d1 --- /dev/null +++ b/resource-manager/main.cpp @@ -0,0 +1,57 @@ + + +#include "Image_asset.h" +#include "resource_manager.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +int main(){ + SDL_Init(SDL_INIT_VIDEO); + ResourceManager* rm = new ResourceManager; + + + Image* img = static_cast(rm->Load("../img.png")); + //Resource* sound = rm->Load("/sound.ogg"); + + + bool quit = false; + + SDL_Event event; + + SDL_Window* window = SDL_CreateWindow("Tessting resources", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); + + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0); + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, img->surface); + + + while (!quit) { + SDL_WaitEvent(&event); + + switch (event.type) { + case SDL_QUIT: + quit = true; + break; + } + + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + + SDL_Delay(100); + + } + SDL_DestroyTexture(texture); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + + + return 0; +} diff --git a/resource-manager/resource.h b/resource-manager/resource.h new file mode 100644 index 0000000..6bc44ed --- /dev/null +++ b/resource-manager/resource.h @@ -0,0 +1,11 @@ +#pragma once + + + + +class Resource{ +public: + + virtual ~Resource() =default; + +}; diff --git a/resource-manager/resource_fabricator.cpp b/resource-manager/resource_fabricator.cpp new file mode 100644 index 0000000..03e3f74 --- /dev/null +++ b/resource-manager/resource_fabricator.cpp @@ -0,0 +1,30 @@ + + +#include +#include + +#include "resource_fabricator.h" +#include "resource.h" +#include "Image_asset.h" +#include "Audio_asset.h" + +#include + + +Resource* ResourceFactory::create_resource(const Constants::FILE_PATH &file_path){ + + std::string extension = std::filesystem::path(file_path).extension(); + + + + if( extension == Constants::PNG_EXT ) { + Image* img = new Image; + img->surface = IMG_Load(file_path.c_str()); + return img; + } + else if ( extension == Constants::OGG_EXT ){ + return new Audio; + } + + return nullptr; +} diff --git a/resource-manager/resource_fabricator.h b/resource-manager/resource_fabricator.h new file mode 100644 index 0000000..3489341 --- /dev/null +++ b/resource-manager/resource_fabricator.h @@ -0,0 +1,23 @@ +#pragma once + + + +#include "resource.h" +#include "constants.h" + + +using namespace crepe; + + +class ResourceFactory { + +public: + + static Resource* create_resource(const Constants::FILE_PATH& file_path); + + +}; + + + + diff --git a/resource-manager/resource_manager.cpp b/resource-manager/resource_manager.cpp new file mode 100644 index 0000000..ce4d5dc --- /dev/null +++ b/resource-manager/resource_manager.cpp @@ -0,0 +1,48 @@ + + +#include "resource_manager.h" +#include "constants.h" +#include "resource.h" +#include "resource_fabricator.h" +#include +#include + + +ResourceManager::ResourceManager(){ + IMG_Init(IMG_INIT_PNG); +} + +ResourceManager::~ResourceManager(){ + + for(auto pair : m_resources){ + delete pair.second; + } + + m_resources.clear(); + + IMG_Quit(); +} + + +Resource* ResourceManager::Load(const Constants::FILE_PATH& file_path){ + + if(m_resources.find(file_path) != m_resources.end( )){ + return m_resources[file_path]; + } + + Resource* res = ResourceFactory::create_resource(file_path); + if(res){ + m_resources[file_path] = std::move(res); + } + + return m_resources[file_path]; +} + + +void ResourceManager::Unload(const Constants::FILE_PATH& file_path){ + std::unordered_map::iterator itr = m_resources.find(file_path); + if(itr != m_resources.end()){ + delete itr->second; + m_resources.erase(itr); + } +} diff --git a/resource-manager/resource_manager.h b/resource-manager/resource_manager.h new file mode 100644 index 0000000..7a86360 --- /dev/null +++ b/resource-manager/resource_manager.h @@ -0,0 +1,32 @@ +#pragma once + + + +#include +#include + + +#include "resource.h" +#include "constants.h" + +using namespace crepe; + + + +class ResourceManager{ + +public: + + ResourceManager(); + ~ResourceManager(); + + Resource* Load(const Constants::FILE_PATH& file_path); + void Unload(const Constants::FILE_PATH& file_path); + + +private: + std::unordered_map m_resources; + + + +}; -- cgit v1.2.3 From 507a97739726feb74ffa91e317ead1773183ccbf Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Mon, 16 Sep 2024 17:40:04 +0200 Subject: added ignore cmake --- .gitignore | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitignore b/.gitignore index ec69436..27d5402 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,16 @@ build doxygen .cache + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json -- cgit v1.2.3 From 765485ced528ca2f4cf644a1503b9446c5826731 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Sun, 22 Sep 2024 10:58:21 +0200 Subject: spritesheet werkt --- resource-manager/CMakeLists.txt | 1 + resource-manager/Image_asset.cpp | 15 +++++++-- resource-manager/Image_asset.h | 10 ++++-- resource-manager/main.cpp | 51 +++++++++++++++++-------------- resource-manager/resource_fabricator.cpp | 6 +--- resource-manager/resource_manager.cpp | 15 --------- resource-manager/resource_manager.h | 18 +++++++++-- resource-manager/spritesheet.cpp | 38 +++++++++++++++++++++++ resource-manager/spritesheet.h | 27 ++++++++++++++++ resource-manager/spritesheet_test.png | Bin 0 -> 616 bytes resource-manager/test.cpp | 0 11 files changed, 131 insertions(+), 50 deletions(-) create mode 100644 resource-manager/spritesheet.cpp create mode 100644 resource-manager/spritesheet.h create mode 100644 resource-manager/spritesheet_test.png create mode 100644 resource-manager/test.cpp diff --git a/resource-manager/CMakeLists.txt b/resource-manager/CMakeLists.txt index 5c2c962..c25ae2b 100644 --- a/resource-manager/CMakeLists.txt +++ b/resource-manager/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(main Image_asset.cpp resource_fabricator.cpp resource_manager.cpp + spritesheet.cpp constants.cpp ) diff --git a/resource-manager/Image_asset.cpp b/resource-manager/Image_asset.cpp index 77eee2f..10b35f8 100644 --- a/resource-manager/Image_asset.cpp +++ b/resource-manager/Image_asset.cpp @@ -1,12 +1,21 @@ #include "Image_asset.h" +#include "spritesheet.h" #include +#include +#include +Image::Image(const std::string& path){ + surface = IMG_Load(path.c_str()); +} + Image::~Image(){ - if (surface) { - SDL_FreeSurface(surface); - } + SDL_FreeSurface(surface); +} + +SDL_Surface* Image::getSurface() const { + return surface; } diff --git a/resource-manager/Image_asset.h b/resource-manager/Image_asset.h index e7d13e9..8563352 100644 --- a/resource-manager/Image_asset.h +++ b/resource-manager/Image_asset.h @@ -3,7 +3,9 @@ #include "resource.h" +#include "spritesheet.h" #include +#include @@ -12,8 +14,12 @@ class Image : public Resource { public: - SDL_Surface* surface = nullptr; - + Image(const std::string& path); ~Image(); + SDL_Surface* getSurface() const; + +private: + SDL_Surface* surface; }; + diff --git a/resource-manager/main.cpp b/resource-manager/main.cpp index 62426d1..6dc362b 100644 --- a/resource-manager/main.cpp +++ b/resource-manager/main.cpp @@ -2,56 +2,61 @@ #include "Image_asset.h" #include "resource_manager.h" +#include "spritesheet.h" #include #include #include #include +#include #include #include #include #include - -int main(){ +int main() { SDL_Init(SDL_INIT_VIDEO); - ResourceManager* rm = new ResourceManager; - + ResourceManager * rm = new ResourceManager; - Image* img = static_cast(rm->Load("../img.png")); + //Image* img = rm->Load("../spritesheet_test.png"); //Resource* sound = rm->Load("/sound.ogg"); - bool quit = false; - + SDL_Event event; - SDL_Window* window = SDL_CreateWindow("Tessting resources", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); + SDL_Window * window + = SDL_CreateWindow("Tessting resources", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); - SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0); - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, img->surface); + SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0); + SpriteSheet spritesheet("../spritesheet_test.png", *renderer, 1, 4); + SDL_SetRenderDrawColor(renderer, 168, 230, 255, 255); + SDL_RenderClear(renderer); while (!quit) { - SDL_WaitEvent(&event); - - switch (event.type) { - case SDL_QUIT: - quit = true; - break; + Uint32 ticks = SDL_GetTicks(); + int sprite = (ticks / 100) % 4; + + //SDL_Rect srcrect = { sprite * 32, 0, 32, 64 }; + SDL_Rect dstrect = {10, 10, 32, 64}; + + while (SDL_PollEvent(&event) != NULL) { + switch (event.type) { + case SDL_QUIT: + quit = true; + break; + } } - SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderClear(renderer); + spritesheet.select_sprite(sprite, 0); + spritesheet.draw_selected_sprite(renderer, &dstrect); SDL_RenderPresent(renderer); - - SDL_Delay(100); - } - SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); - - return 0; } diff --git a/resource-manager/resource_fabricator.cpp b/resource-manager/resource_fabricator.cpp index 03e3f74..2b1f5f7 100644 --- a/resource-manager/resource_fabricator.cpp +++ b/resource-manager/resource_fabricator.cpp @@ -15,12 +15,8 @@ Resource* ResourceFactory::create_resource(const Constants::FILE_PATH &file_path std::string extension = std::filesystem::path(file_path).extension(); - - if( extension == Constants::PNG_EXT ) { - Image* img = new Image; - img->surface = IMG_Load(file_path.c_str()); - return img; + return new Image(file_path); } else if ( extension == Constants::OGG_EXT ){ return new Audio; diff --git a/resource-manager/resource_manager.cpp b/resource-manager/resource_manager.cpp index ce4d5dc..e8828a4 100644 --- a/resource-manager/resource_manager.cpp +++ b/resource-manager/resource_manager.cpp @@ -24,21 +24,6 @@ ResourceManager::~ResourceManager(){ } -Resource* ResourceManager::Load(const Constants::FILE_PATH& file_path){ - - if(m_resources.find(file_path) != m_resources.end( )){ - return m_resources[file_path]; - } - - Resource* res = ResourceFactory::create_resource(file_path); - if(res){ - m_resources[file_path] = std::move(res); - } - - return m_resources[file_path]; -} - - void ResourceManager::Unload(const Constants::FILE_PATH& file_path){ std::unordered_map::iterator itr = m_resources.find(file_path); if(itr != m_resources.end()){ diff --git a/resource-manager/resource_manager.h b/resource-manager/resource_manager.h index 7a86360..fd6657c 100644 --- a/resource-manager/resource_manager.h +++ b/resource-manager/resource_manager.h @@ -2,12 +2,14 @@ +#include #include #include #include "resource.h" #include "constants.h" +#include "resource_fabricator.h" using namespace crepe; @@ -20,9 +22,21 @@ public: ResourceManager(); ~ResourceManager(); - Resource* Load(const Constants::FILE_PATH& file_path); - void Unload(const Constants::FILE_PATH& file_path); + template + T* Load(const Constants::FILE_PATH& file_path){ + + if (m_resources.find(file_path) != m_resources.end()) { + return static_cast(m_resources[file_path]); + } + + Resource* resource = ResourceFactory::create_resource(file_path); + if (resource) { + m_resources[file_path] = std::move(resource); + } + return static_cast(m_resources[file_path]); + } + void Unload(const Constants::FILE_PATH& file_path); private: std::unordered_map m_resources; diff --git a/resource-manager/spritesheet.cpp b/resource-manager/spritesheet.cpp new file mode 100644 index 0000000..c8e1b70 --- /dev/null +++ b/resource-manager/spritesheet.cpp @@ -0,0 +1,38 @@ + + +#include "spritesheet.h" + +#include +#include +#include +#include +#include +#include + + +SpriteSheet::SpriteSheet(const std::string& path, SDL_Renderer& renderer, const int row, const int column){ + m_spritesheet_image = IMG_Load(path.c_str()); + texture = SDL_CreateTextureFromSurface(&renderer, m_spritesheet_image); + m_clip.w = m_spritesheet_image->w / column; + m_clip.h = m_spritesheet_image->h / row; + +} + +SpriteSheet::~SpriteSheet(){ + SDL_DestroyTexture(texture); + SDL_FreeSurface(m_spritesheet_image); +} + +void SpriteSheet::select_sprite(const int x, const int y){ + m_clip.x = x * m_clip.w; + m_clip.y = y * m_clip.h; +} + +void SpriteSheet::draw_selected_sprite(SDL_Renderer* window_surface, SDL_Rect* position){ + SDL_RenderCopy(window_surface, texture, &m_clip, position); +} + + +SDL_Surface* SpriteSheet::getSurface() const { + return m_spritesheet_image; +} diff --git a/resource-manager/spritesheet.h b/resource-manager/spritesheet.h new file mode 100644 index 0000000..022b6cf --- /dev/null +++ b/resource-manager/spritesheet.h @@ -0,0 +1,27 @@ +#pragma once + + + + +#include +#include +#include +#include +class SpriteSheet{ + + +public: + SpriteSheet(const std::string& path, SDL_Renderer& renderer, const int row, const int column); + ~SpriteSheet(); + + + void select_sprite(const int x, const int y); + void draw_selected_sprite(SDL_Renderer* window_surface, SDL_Rect* position); + SDL_Surface* getSurface() const; + +private: + SDL_Rect m_clip; + SDL_Surface* m_spritesheet_image; + SDL_Texture * texture; + +}; diff --git a/resource-manager/spritesheet_test.png b/resource-manager/spritesheet_test.png new file mode 100644 index 0000000..d68a72a Binary files /dev/null and b/resource-manager/spritesheet_test.png differ diff --git a/resource-manager/test.cpp b/resource-manager/test.cpp new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3 From b7df77d6cc26cb9ee46891d7108f01734b3104dd Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sun, 22 Sep 2024 17:08:56 +0200 Subject: add miniaudio poc --- .gitmodules | 3 +++ lib/miniaudio/CMakeLists.txt | 10 ++++++++ lib/miniaudio/lib | 1 + lib/miniaudio/main.c | 2 ++ mwe/audio/bgm.ogg | Bin 0 -> 125020 bytes mwe/audio/miniaudio/CMakeLists.txt | 19 +++++++++++++++ mwe/audio/miniaudio/main.cpp | 46 +++++++++++++++++++++++++++++++++++++ mwe/audio/sfx1.wav | Bin 0 -> 44144 bytes mwe/audio/sfx2.wav | Bin 0 -> 44144 bytes mwe/audio/sfx3.wav | Bin 0 -> 44144 bytes 10 files changed, 81 insertions(+) create mode 100644 lib/miniaudio/CMakeLists.txt create mode 160000 lib/miniaudio/lib create mode 100644 lib/miniaudio/main.c create mode 100644 mwe/audio/bgm.ogg create mode 100644 mwe/audio/miniaudio/CMakeLists.txt create mode 100644 mwe/audio/miniaudio/main.cpp create mode 100644 mwe/audio/sfx1.wav create mode 100644 mwe/audio/sfx2.wav create mode 100644 mwe/audio/sfx3.wav diff --git a/.gitmodules b/.gitmodules index f8a9861..f93e48c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,3 +3,6 @@ url = https://github.com/google/googletest branch = v1.15.x shallow = true +[submodule "lib/miniaudio/lib"] + path = lib/miniaudio/lib + url = https://github.com/mackron/miniaudio diff --git a/lib/miniaudio/CMakeLists.txt b/lib/miniaudio/CMakeLists.txt new file mode 100644 index 0000000..65d416c --- /dev/null +++ b/lib/miniaudio/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.28) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) + +project(miniaudio C CXX) + +add_library(miniaudio STATIC main.c) +target_include_directories(miniaudio SYSTEM INTERFACE lib) + diff --git a/lib/miniaudio/lib b/lib/miniaudio/lib new file mode 160000 index 0000000..4a5b74b --- /dev/null +++ b/lib/miniaudio/lib @@ -0,0 +1 @@ +Subproject commit 4a5b74bef029b3592c54b6048650ee5f972c1a48 diff --git a/lib/miniaudio/main.c b/lib/miniaudio/main.c new file mode 100644 index 0000000..8d898f7 --- /dev/null +++ b/lib/miniaudio/main.c @@ -0,0 +1,2 @@ +#define MINIAUDIO_IMPLEMENTATION +#include "lib/miniaudio.h" diff --git a/mwe/audio/bgm.ogg b/mwe/audio/bgm.ogg new file mode 100644 index 0000000..d6397fb Binary files /dev/null and b/mwe/audio/bgm.ogg differ diff --git a/mwe/audio/miniaudio/CMakeLists.txt b/mwe/audio/miniaudio/CMakeLists.txt new file mode 100644 index 0000000..6dd0191 --- /dev/null +++ b/mwe/audio/miniaudio/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.28) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) +set(CMAKE_BUILD_TYPE Debug) + +add_subdirectory(../../../lib/miniaudio miniaudio) + +project(poc C CXX) + +add_executable(main + main.cpp +) + +target_link_libraries(main + miniaudio +) + diff --git a/mwe/audio/miniaudio/main.cpp b/mwe/audio/miniaudio/main.cpp new file mode 100644 index 0000000..bf31898 --- /dev/null +++ b/mwe/audio/miniaudio/main.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include + +using namespace std; +using namespace std::chrono_literals; + +int main() { + ma_engine engine; + ma_engine_init(NULL, &engine); + + // 1. load background track (ogg vorbis) + ma_sound bgm; + ma_sound_init_from_file(&engine, "../bgm.ogg", 0, NULL, NULL, &bgm); + + // 2. load samples (wav) + ma_sound sfx[3]; + ma_sound_init_from_file(&engine, "../sfx1.wav", 0, NULL, NULL, &sfx[0]); + ma_sound_init_from_file(&engine, "../sfx2.wav", 0, NULL, NULL, &sfx[1]); + ma_sound_init_from_file(&engine, "../sfx3.wav", 0, NULL, NULL, &sfx[2]); + + // 3. start the background track + ma_sound_start(&bgm); + + // 4. play samples sequentially while controlling the background track + this_thread::sleep_for(500ms); + ma_sound_start(&sfx[0]); + this_thread::sleep_for(500ms); + ma_sound_start(&sfx[1]); + ma_sound_stop(&bgm); + this_thread::sleep_for(500ms); + ma_sound_start(&sfx[2]); + ma_sound_start(&bgm); // this actually resumes now + this_thread::sleep_for(500ms); + for (unsigned i = 0; i < 3; i++) + ma_sound_seek_to_pcm_frame(&sfx[i], 0); + + // 5. play all samples simultaniously + for (unsigned i = 0; i < 3; i++) + ma_sound_start(&sfx[i]); + this_thread::sleep_for(1000ms); + + ma_engine_uninit(&engine); + return 0; +} diff --git a/mwe/audio/sfx1.wav b/mwe/audio/sfx1.wav new file mode 100644 index 0000000..576377a Binary files /dev/null and b/mwe/audio/sfx1.wav differ diff --git a/mwe/audio/sfx2.wav b/mwe/audio/sfx2.wav new file mode 100644 index 0000000..908ec86 Binary files /dev/null and b/mwe/audio/sfx2.wav differ diff --git a/mwe/audio/sfx3.wav b/mwe/audio/sfx3.wav new file mode 100644 index 0000000..20b7c84 Binary files /dev/null and b/mwe/audio/sfx3.wav differ -- cgit v1.2.3 From 35224c07549448e919a18a463e5dec81bbabf7c6 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Wed, 25 Sep 2024 13:07:13 +0200 Subject: spritesheet and image changed --- resource-manager/Audio_asset.h | 5 +++++ resource-manager/Image_asset.cpp | 25 ++++++++++++++++++------- resource-manager/Image_asset.h | 13 ++++++++----- resource-manager/main.cpp | 22 ++++++++++------------ resource-manager/resource_fabricator.cpp | 22 ---------------------- resource-manager/resource_fabricator.h | 11 +++++++++-- resource-manager/resource_manager.cpp | 2 ++ resource-manager/resource_manager.h | 15 +++++++++++---- resource-manager/spritesheet.cpp | 19 ++++++++++--------- resource-manager/spritesheet.h | 14 +++++++++----- 10 files changed, 82 insertions(+), 66 deletions(-) diff --git a/resource-manager/Audio_asset.h b/resource-manager/Audio_asset.h index 87fa762..7f7f8cd 100644 --- a/resource-manager/Audio_asset.h +++ b/resource-manager/Audio_asset.h @@ -2,10 +2,15 @@ #include "resource.h" +#include class Audio : public Resource { +public: + Audio(const std::string& path); + ~Audio(); + diff --git a/resource-manager/Image_asset.cpp b/resource-manager/Image_asset.cpp index 10b35f8..791e988 100644 --- a/resource-manager/Image_asset.cpp +++ b/resource-manager/Image_asset.cpp @@ -1,21 +1,32 @@ #include "Image_asset.h" -#include "spritesheet.h" #include #include +#include #include -Image::Image(const std::string& path){ - surface = IMG_Load(path.c_str()); +Texture::Texture(const std::string& path){ + m_surface = IMG_Load(path.c_str()); } +void Texture::setTexture(SDL_Renderer& renderer){ + m_texture = SDL_CreateTextureFromSurface(&renderer, m_surface); +} + + +Texture::~Texture(){ + SDL_FreeSurface(m_surface); + + if(m_texture) + SDL_DestroyTexture(m_texture); +} -Image::~Image(){ - SDL_FreeSurface(surface); +SDL_Surface* Texture::getSurface() const { + return m_surface; } -SDL_Surface* Image::getSurface() const { - return surface; +SDL_Texture* Texture::getTexture() const{ + return m_texture; } diff --git a/resource-manager/Image_asset.h b/resource-manager/Image_asset.h index 8563352..88c0fb0 100644 --- a/resource-manager/Image_asset.h +++ b/resource-manager/Image_asset.h @@ -3,7 +3,7 @@ #include "resource.h" -#include "spritesheet.h" +#include #include #include @@ -11,15 +11,18 @@ -class Image : public Resource { +class Texture : public Resource { public: - Image(const std::string& path); - ~Image(); + Texture(const std::string& path); + ~Texture(); + void setTexture(SDL_Renderer& renderer); SDL_Surface* getSurface() const; + SDL_Texture* getTexture() const; private: - SDL_Surface* surface; + SDL_Surface* m_surface; + SDL_Texture* m_texture; }; diff --git a/resource-manager/main.cpp b/resource-manager/main.cpp index 6dc362b..dc087c6 100644 --- a/resource-manager/main.cpp +++ b/resource-manager/main.cpp @@ -15,10 +15,6 @@ int main() { SDL_Init(SDL_INIT_VIDEO); - ResourceManager * rm = new ResourceManager; - - //Image* img = rm->Load("../spritesheet_test.png"); - //Resource* sound = rm->Load("/sound.ogg"); bool quit = false; @@ -28,18 +24,20 @@ int main() { = SDL_CreateWindow("Tessting resources", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); + SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0); - SpriteSheet spritesheet("../spritesheet_test.png", *renderer, 1, 4); + ResourceManager * rm = new ResourceManager(); + + Texture* img = rm->Load("../img.png"); + img->setTexture(*renderer); + + SpriteSheet* SS = rm->Load("../spritesheet_test.png"); + SDL_SetRenderDrawColor(renderer, 168, 230, 255, 255); SDL_RenderClear(renderer); while (!quit) { - Uint32 ticks = SDL_GetTicks(); - int sprite = (ticks / 100) % 4; - - //SDL_Rect srcrect = { sprite * 32, 0, 32, 64 }; - SDL_Rect dstrect = {10, 10, 32, 64}; while (SDL_PollEvent(&event) != NULL) { switch (event.type) { @@ -49,9 +47,9 @@ int main() { } } + SDL_RenderClear(renderer); - spritesheet.select_sprite(sprite, 0); - spritesheet.draw_selected_sprite(renderer, &dstrect); + SDL_RenderCopy(renderer, img->getTexture(), NULL, NULL); SDL_RenderPresent(renderer); } SDL_DestroyRenderer(renderer); diff --git a/resource-manager/resource_fabricator.cpp b/resource-manager/resource_fabricator.cpp index 2b1f5f7..36353e4 100644 --- a/resource-manager/resource_fabricator.cpp +++ b/resource-manager/resource_fabricator.cpp @@ -1,26 +1,4 @@ -#include -#include - #include "resource_fabricator.h" -#include "resource.h" -#include "Image_asset.h" -#include "Audio_asset.h" - -#include - - -Resource* ResourceFactory::create_resource(const Constants::FILE_PATH &file_path){ - - std::string extension = std::filesystem::path(file_path).extension(); - - if( extension == Constants::PNG_EXT ) { - return new Image(file_path); - } - else if ( extension == Constants::OGG_EXT ){ - return new Audio; - } - return nullptr; -} diff --git a/resource-manager/resource_fabricator.h b/resource-manager/resource_fabricator.h index 3489341..2d46b00 100644 --- a/resource-manager/resource_fabricator.h +++ b/resource-manager/resource_fabricator.h @@ -2,8 +2,13 @@ +#include "Audio_asset.h" +#include "Image_asset.h" #include "resource.h" #include "constants.h" +#include "spritesheet.h" +#include + using namespace crepe; @@ -13,8 +18,10 @@ class ResourceFactory { public: - static Resource* create_resource(const Constants::FILE_PATH& file_path); - + template + static Resource* create_resource(const Constants::FILE_PATH& file_path){ + return new T(file_path); + } }; diff --git a/resource-manager/resource_manager.cpp b/resource-manager/resource_manager.cpp index e8828a4..99918d1 100644 --- a/resource-manager/resource_manager.cpp +++ b/resource-manager/resource_manager.cpp @@ -5,6 +5,7 @@ #include "resource.h" #include "resource_fabricator.h" #include +#include #include @@ -31,3 +32,4 @@ void ResourceManager::Unload(const Constants::FILE_PATH& file_path){ m_resources.erase(itr); } } + diff --git a/resource-manager/resource_manager.h b/resource-manager/resource_manager.h index fd6657c..5570313 100644 --- a/resource-manager/resource_manager.h +++ b/resource-manager/resource_manager.h @@ -2,8 +2,7 @@ -#include -#include +#include #include @@ -11,6 +10,15 @@ #include "constants.h" #include "resource_fabricator.h" + +enum class asset_type{ + TEXTURE, + SPRITESHEET, + AUDIO, + MAP, + UNKNOWN, +}; + using namespace crepe; @@ -29,7 +37,7 @@ public: return static_cast(m_resources[file_path]); } - Resource* resource = ResourceFactory::create_resource(file_path); + Resource* resource = ResourceFactory::create_resource(file_path); if (resource) { m_resources[file_path] = std::move(resource); } @@ -42,5 +50,4 @@ private: std::unordered_map m_resources; - }; diff --git a/resource-manager/spritesheet.cpp b/resource-manager/spritesheet.cpp index c8e1b70..c0e4d06 100644 --- a/resource-manager/spritesheet.cpp +++ b/resource-manager/spritesheet.cpp @@ -1,6 +1,7 @@ #include "spritesheet.h" +#include "Image_asset.h" #include #include @@ -10,17 +11,17 @@ #include -SpriteSheet::SpriteSheet(const std::string& path, SDL_Renderer& renderer, const int row, const int column){ - m_spritesheet_image = IMG_Load(path.c_str()); - texture = SDL_CreateTextureFromSurface(&renderer, m_spritesheet_image); - m_clip.w = m_spritesheet_image->w / column; - m_clip.h = m_spritesheet_image->h / row; +SpriteSheet::SpriteSheet(const std::string& path){ + m_spritesheet = new Texture(path); +} +void SpriteSheet::set_spritesheet_data(SDL_Renderer& renderer, const int row, const int column){ + m_clip.w = m_spritesheet->getSurface()->w / column; + m_clip.h = m_spritesheet->getSurface()->h / row; } SpriteSheet::~SpriteSheet(){ - SDL_DestroyTexture(texture); - SDL_FreeSurface(m_spritesheet_image); + delete m_spritesheet; } void SpriteSheet::select_sprite(const int x, const int y){ @@ -29,10 +30,10 @@ void SpriteSheet::select_sprite(const int x, const int y){ } void SpriteSheet::draw_selected_sprite(SDL_Renderer* window_surface, SDL_Rect* position){ - SDL_RenderCopy(window_surface, texture, &m_clip, position); + SDL_RenderCopy(window_surface, m_spritesheet->getTexture(), &m_clip, position); } SDL_Surface* SpriteSheet::getSurface() const { - return m_spritesheet_image; + return m_spritesheet->getSurface(); } diff --git a/resource-manager/spritesheet.h b/resource-manager/spritesheet.h index 022b6cf..be1a051 100644 --- a/resource-manager/spritesheet.h +++ b/resource-manager/spritesheet.h @@ -3,17 +3,23 @@ +#include "Image_asset.h" +#include "resource.h" #include #include #include #include -class SpriteSheet{ + + +class SpriteSheet : public Resource{ public: - SpriteSheet(const std::string& path, SDL_Renderer& renderer, const int row, const int column); + SpriteSheet(const std::string& path); ~SpriteSheet(); + void set_spritesheet_data(SDL_Renderer& renderer, const int row, const int column); + void select_sprite(const int x, const int y); void draw_selected_sprite(SDL_Renderer* window_surface, SDL_Rect* position); @@ -21,7 +27,5 @@ public: private: SDL_Rect m_clip; - SDL_Surface* m_spritesheet_image; - SDL_Texture * texture; - + Texture* m_spritesheet; }; -- cgit v1.2.3 From 9ab843567b27bd23eb7e0d2d4a48b78f7df93595 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Wed, 25 Sep 2024 16:31:22 +0200 Subject: testing map implementation --- resource-manager/CMakeLists.txt | 13 +- resource-manager/Image_asset.cpp | 13 +- resource-manager/Image_asset.h | 2 +- resource-manager/TextureMap.cpp | 74 + resource-manager/TextureMap.h | 28 + resource-manager/cmake/modules/FindTMXLITE.cmake | 10 + resource-manager/main.cpp | 15 +- resource-manager/map_asset.cpp | 69 + resource-manager/map_asset.h | 35 + resource-manager/map_layer.cpp | 109 + resource-manager/map_layer.h | 26 + resource-manager/spritesheet.cpp | 1 + resource-manager/stb_image.h | 7985 ++++++++++++++++++++++ 13 files changed, 8367 insertions(+), 13 deletions(-) create mode 100644 resource-manager/TextureMap.cpp create mode 100644 resource-manager/TextureMap.h create mode 100644 resource-manager/cmake/modules/FindTMXLITE.cmake create mode 100644 resource-manager/map_asset.cpp create mode 100644 resource-manager/map_asset.h create mode 100644 resource-manager/map_layer.cpp create mode 100644 resource-manager/map_layer.h create mode 100644 resource-manager/stb_image.h diff --git a/resource-manager/CMakeLists.txt b/resource-manager/CMakeLists.txt index c25ae2b..cf883ae 100644 --- a/resource-manager/CMakeLists.txt +++ b/resource-manager/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.28) +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") # Set C and C++ standards set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) @@ -12,10 +13,11 @@ add_compile_definitions(DEBUG) # Project name and supported languages project(crepe C CXX) -# Find SDL2 +find_package(TMXLITE REQUIRED) find_package(SDL2 REQUIRED) find_package(SDL2_image REQUIRED) + # Add all source files to the executable add_executable(main main.cpp @@ -25,11 +27,12 @@ add_executable(main resource_manager.cpp spritesheet.cpp constants.cpp + map_asset.cpp + map_layer.cpp + TextureMap.cpp ) -# Include directories for headers -target_include_directories(main PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_IMAGE_INCLUDE_DIRS} .) +target_include_directories(main PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_IMAGE_INCLUDE_DIRS} ${TMXLITE_INCLUDE_DIR} .) -# Link SDL2 library -target_link_libraries(main PRIVATE ${SDL2_LIBRARIES} SDL2_image::SDL2_image) +target_link_libraries(main PRIVATE ${SDL2_LIBRARIES} SDL2_image::SDL2_image ${TMXLITE_LIBRARIES} ) diff --git a/resource-manager/Image_asset.cpp b/resource-manager/Image_asset.cpp index 791e988..c5599e9 100644 --- a/resource-manager/Image_asset.cpp +++ b/resource-manager/Image_asset.cpp @@ -1,10 +1,9 @@ #include "Image_asset.h" -#include #include -#include -#include +#include + Texture::Texture(const std::string& path){ @@ -15,12 +14,14 @@ void Texture::setTexture(SDL_Renderer& renderer){ m_texture = SDL_CreateTextureFromSurface(&renderer, m_surface); } - Texture::~Texture(){ - SDL_FreeSurface(m_surface); + if (m_surface) { + SDL_FreeSurface(m_surface); + } - if(m_texture) + if(m_texture){ SDL_DestroyTexture(m_texture); + } } SDL_Surface* Texture::getSurface() const { diff --git a/resource-manager/Image_asset.h b/resource-manager/Image_asset.h index 88c0fb0..93617e6 100644 --- a/resource-manager/Image_asset.h +++ b/resource-manager/Image_asset.h @@ -16,7 +16,7 @@ class Texture : public Resource { public: Texture(const std::string& path); ~Texture(); - + void setTexture(SDL_Renderer& renderer); SDL_Surface* getSurface() const; SDL_Texture* getTexture() const; diff --git a/resource-manager/TextureMap.cpp b/resource-manager/TextureMap.cpp new file mode 100644 index 0000000..847bab9 --- /dev/null +++ b/resource-manager/TextureMap.cpp @@ -0,0 +1,74 @@ +#include "TextureMap.h" + + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#include +#include + +TextureMap::TextureMap() + : m_texture(nullptr) +{ + +} + +TextureMap::~TextureMap() +{ + if (m_texture) + { + SDL_DestroyTexture(m_texture); + } +} + +//public +bool TextureMap::loadFromFile(const std::string& path, SDL_Renderer* renderer) +{ + assert(renderer != nullptr); + assert(!path.empty()); + + std::int32_t x = 0; + std::int32_t y = 0; + std::int32_t c = 0; + unsigned char* data = stbi_load(path.c_str(), &x, &y, &c, 0); + + if (data) + { + std::int32_t pitch = x * c; + pitch = (pitch + 3) & ~3; + + constexpr std::int32_t rmask = 0x000000ff; + constexpr std::int32_t gmask = 0x0000ff00; + constexpr std::int32_t bmask = 0x00ff0000; + const std::int32_t amask = c == 4 ? 0xff000000 : 0; + + auto* surface = SDL_CreateRGBSurfaceFrom(data, x, y, c * 8, pitch, rmask, gmask, bmask, amask); + + if (!surface) + { + std::cerr << "Unable to create texture surface: " << SDL_GetError() << "\n"; + stbi_image_free(data); + return false; + } + + m_texture = SDL_CreateTextureFromSurface(renderer, surface); + + SDL_FreeSurface(surface); + stbi_image_free(data); + + if (!m_texture) + { + std::cerr << "Failed to create texture for " << path << "\n"; + return false; + } + + //sets alpha blending + //SDL_SetTextureBlendMode(m_texture, SDL_BLENDMODE_BLEND); + m_size.x = x; + m_size.y = y; + + return true; + } + + return false; +} diff --git a/resource-manager/TextureMap.h b/resource-manager/TextureMap.h new file mode 100644 index 0000000..dc8b8d3 --- /dev/null +++ b/resource-manager/TextureMap.h @@ -0,0 +1,28 @@ +#pragma once + + +#include +#include +#include + +class TextureMap final +{ +public: + TextureMap(); + ~TextureMap(); + + TextureMap(const TextureMap&) = delete; + TextureMap(TextureMap&&) = delete; + + TextureMap& operator = (const TextureMap&) = delete; + TextureMap& operator = (TextureMap&&) = delete; + + bool loadFromFile(const std::string&, SDL_Renderer*); + SDL_Point getSize() const { return m_size; } + + operator SDL_Texture* () { return m_texture; } + +private: + SDL_Texture* m_texture; + SDL_Point m_size; +}; diff --git a/resource-manager/cmake/modules/FindTMXLITE.cmake b/resource-manager/cmake/modules/FindTMXLITE.cmake new file mode 100644 index 0000000..6ca7154 --- /dev/null +++ b/resource-manager/cmake/modules/FindTMXLITE.cmake @@ -0,0 +1,10 @@ +find_path(TMXLITE_INCLUDE_DIR NAMES tmxlite/Config.hpp PATH_SUFFIXES include) + +find_library(TMXLITE_LIBRARY_DEBUG NAMES tmxlite-d) +find_library(TMXLITE_LIBRARY_RELEASE NAMES tmxlite) + +include(SelectLibraryConfigurations) +select_library_configurations(TMXLITE) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(TMXLITE DEFAULT_MSG TMXLITE_LIBRARY TMXLITE_INCLUDE_DIR) \ No newline at end of file diff --git a/resource-manager/main.cpp b/resource-manager/main.cpp index dc087c6..d0e8312 100644 --- a/resource-manager/main.cpp +++ b/resource-manager/main.cpp @@ -1,11 +1,13 @@ #include "Image_asset.h" +#include "map_asset.h" #include "resource_manager.h" #include "spritesheet.h" #include #include #include +#include #include #include #include @@ -29,15 +31,25 @@ int main() { ResourceManager * rm = new ResourceManager(); + /* Texture* img = rm->Load("../img.png"); img->setTexture(*renderer); SpriteSheet* SS = rm->Load("../spritesheet_test.png"); + SS->set_spritesheet_data(*renderer, 1, 4); + */ + + Map* map = rm->Load("../../asset/tiled/demo.tmx"); + map->SetRenderer(*renderer); + + SDL_SetRenderDrawColor(renderer, 168, 230, 255, 255); SDL_RenderClear(renderer); while (!quit) { + Uint32 ticks = SDL_GetTicks(); + int sprite = (ticks / 100) % 4; while (SDL_PollEvent(&event) != NULL) { switch (event.type) { @@ -49,9 +61,10 @@ int main() { SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, img->getTexture(), NULL, NULL); SDL_RenderPresent(renderer); } + delete rm; + SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); diff --git a/resource-manager/map_asset.cpp b/resource-manager/map_asset.cpp new file mode 100644 index 0000000..ab19e59 --- /dev/null +++ b/resource-manager/map_asset.cpp @@ -0,0 +1,69 @@ + + + + +#include "map_asset.h" +#include "TextureMap.h" +#include "map_layer.h" +#include +#include +#include +#include +#include +#include +#include + + +Map::Map(const std::string& path){ + if(m_Map.load(path)){ + std::cout << "Map loaded correctly " << std::endl; + } +} + +Map::~Map(){ + for(const auto& r : m_MapTextures){ + delete r; + } + m_MapTextures.clear(); + + for(const auto& m : m_RenderLayers){ + delete m; + } + m_RenderLayers.clear(); +} + + +void Map::SetRenderer(SDL_Renderer& renderer){ + this->m_Renderer = &renderer; + this->SetMapTextures(); + this->SetMapLayers(); +} + +void Map::draw() const{ + for(const auto& l : m_RenderLayers){ + l->draw(m_Renderer); + } +} + +void Map::SetMapTextures(){ + const auto& tileSets = m_Map.getTilesets(); + assert(~tileSets.empty()); + + for (const auto& ts : tileSets ) { + m_MapTextures.emplace_back(new TextureMap); + if(!m_MapTextures.back()->loadFromFile(ts.getImagePath(), m_Renderer)){ + std::cerr << "Failed opening " << ts.getImagePath() << "\n"; + } + } +} + +void Map::SetMapLayers(){ + const auto& mapLayers = m_Map.getLayers(); + + for(auto i = 0u; i < mapLayers.size(); ++i){ + if (mapLayers[i]->getType() == tmx::Layer::Type::Tile) { + m_RenderLayers.emplace_back(new MapLayer); + m_RenderLayers.back()->create(this->m_Map, i, this->m_MapTextures); + } + } +} diff --git a/resource-manager/map_asset.h b/resource-manager/map_asset.h new file mode 100644 index 0000000..39cb8f4 --- /dev/null +++ b/resource-manager/map_asset.h @@ -0,0 +1,35 @@ +#pragma once + + +#include "TextureMap.h" +#include "map_layer.h" +#include "resource.h" +#include +#include +#include + + +class Map : public Resource { + +public: + Map(const std::string& path); + ~Map(); + + void SetRenderer(SDL_Renderer&); + void draw() const; + + +private: + void SetMapTextures(); + void SetMapLayers(); + + +private: + tmx::Map m_Map; + std::vector m_MapTextures; + std::vector m_RenderLayers; + + SDL_Renderer* m_Renderer; + + +}; diff --git a/resource-manager/map_layer.cpp b/resource-manager/map_layer.cpp new file mode 100644 index 0000000..791972d --- /dev/null +++ b/resource-manager/map_layer.cpp @@ -0,0 +1,109 @@ + + +#include "map_layer.h" +#include "TextureMap.h" +#include +#include + + + +MapLayer::MapLayer(){} + +MapLayer::~MapLayer(){ + m_subsets.clear(); +} + +bool MapLayer::create(const tmx::Map& map, std::uint32_t layerIndex, const std::vector& textures) +{ + const auto& layers = map.getLayers(); + assert(layers[layerIndex]->getType() == tmx::Layer::Type::Tile); + + const auto& layer = layers[layerIndex]->getLayerAs(); + const auto mapSize = map.getTileCount(); + const auto mapTileSize = map.getTileSize(); + const auto& tileSets = map.getTilesets(); + + const auto tintColour = layer.getTintColour(); + const SDL_Colour vertColour = + { + tintColour.r, + tintColour.g, + tintColour.b, + tintColour.a + }; + + for (auto i = 0u; i < tileSets.size(); ++i) + { + //check tile ID to see if it falls within the current tile set + const auto& ts = tileSets[i]; + const auto& tileIDs = layer.getTiles(); + + const auto texSize = textures[i]->getSize(); + const auto tileCountX = texSize.x / mapTileSize.x; + const auto tileCountY = texSize.y / mapTileSize.y; + + const float uNorm = static_cast(mapTileSize.x) / texSize.x; + const float vNorm = static_cast(mapTileSize.y) / texSize.y; + + std::vector verts; + for (auto y = 0u; y < mapSize.y; ++y) + { + for (auto x = 0u; x < mapSize.x; ++x) + { + const auto idx = y * mapSize.x + x; + if (idx < tileIDs.size() && tileIDs[idx].ID >= ts.getFirstGID() + && tileIDs[idx].ID < (ts.getFirstGID() + ts.getTileCount())) + { + //tex coords + auto idIndex = (tileIDs[idx].ID - ts.getFirstGID()); + float u = static_cast(idIndex % tileCountX); + float v = static_cast(idIndex / tileCountY); + u *= mapTileSize.x; //TODO we should be using the tile set size, as this may be different from the map's grid size + v *= mapTileSize.y; + + //normalise the UV + u /= textures[i]->getSize().x; + v /= textures[i]->getSize().y; + + //vert pos + const float tilePosX = static_cast(x) * mapTileSize.x; + const float tilePosY = (static_cast(y) * mapTileSize.y); + + + //push back to vert array + SDL_Vertex vert = { { tilePosX, tilePosY }, vertColour, {u, v} }; + verts.emplace_back(vert); + vert = { { tilePosX + mapTileSize.x, tilePosY }, vertColour, {u + uNorm, v} }; + verts.emplace_back(vert); + vert = { { tilePosX, tilePosY + mapTileSize.y}, vertColour, {u, v + vNorm} }; + verts.emplace_back(vert); + + vert = { { tilePosX, tilePosY +mapTileSize.y}, vertColour, {u, v + vNorm} }; + verts.emplace_back(vert); + vert = { { tilePosX + mapTileSize.x, tilePosY }, vertColour, {u + uNorm, v} }; + verts.emplace_back(vert); + vert = { { tilePosX + mapTileSize.x, tilePosY + mapTileSize.y }, vertColour, {u + uNorm, v + vNorm} }; + verts.emplace_back(vert); + } + } + } + + if (!verts.empty()) + { + m_subsets.emplace_back(); + m_subsets.back().texture = *textures[i]; + m_subsets.back().vertexData.swap(verts); + } + } + + return true; +} + +void MapLayer::draw(SDL_Renderer* renderer) const +{ + assert(renderer); + for (const auto& s : m_subsets) + { + SDL_RenderGeometry(renderer, s.texture, s.vertexData.data(), static_cast(s.vertexData.size()), nullptr, 0); + } +} diff --git a/resource-manager/map_layer.h b/resource-manager/map_layer.h new file mode 100644 index 0000000..e633290 --- /dev/null +++ b/resource-manager/map_layer.h @@ -0,0 +1,26 @@ +#pragma once + + +#include "TextureMap.h" +#include +#include + +class MapLayer final{ + +public: + explicit MapLayer(); + ~MapLayer(); + + bool create(const tmx::Map&, std::uint32_t index, const std::vector& textures); + void draw(SDL_Renderer*) const; + +private: + + struct subset final + { + std::vector vertexData; + SDL_Texture* texture = nullptr; + }; + std::vector m_subsets; + +}; diff --git a/resource-manager/spritesheet.cpp b/resource-manager/spritesheet.cpp index c0e4d06..b6ff60e 100644 --- a/resource-manager/spritesheet.cpp +++ b/resource-manager/spritesheet.cpp @@ -16,6 +16,7 @@ SpriteSheet::SpriteSheet(const std::string& path){ } void SpriteSheet::set_spritesheet_data(SDL_Renderer& renderer, const int row, const int column){ + m_spritesheet->setTexture(renderer); m_clip.w = m_spritesheet->getSurface()->w / column; m_clip.h = m_spritesheet->getSurface()->h / row; } diff --git a/resource-manager/stb_image.h b/resource-manager/stb_image.h new file mode 100644 index 0000000..a632d54 --- /dev/null +++ b/resource-manager/stb_image.h @@ -0,0 +1,7985 @@ +/* stb_image - v2.29 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.29 (2023-05-xx) optimizations + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// 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 *channels_in_file -- outputs # of image components in image file +// int desired_channels -- 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, or NULL on an allocation failure or if the image is +// corrupt or invalid. 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 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// 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, *channels_in_file 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. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// 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). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. 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); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// 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). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +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,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#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. +STBIDEF 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" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#if defined(_MSC_VER) || defined(__SYMBIAN32__) +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#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) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__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; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *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->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *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; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two ints fits in a signed short, 0 on overflow. +static int stbi__mul2shorts_valid(int a, int b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *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; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (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; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (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; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// 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 stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("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 STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__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 (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__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 (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("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, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("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]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__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] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - 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 +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// 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; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__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 progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } + 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++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + 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] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + 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 const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__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)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__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]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// 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 const stbi_uc stbi__jpeg_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 stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("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 ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("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 + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__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 (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__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) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__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)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++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]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[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 + STBI__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] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__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 stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__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 = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // 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 stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(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) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(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; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // 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) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + 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) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + 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); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // 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) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + 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) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__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; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + 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 stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("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; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // 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; + // these sizes can't be more than 17 bits + 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 + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + 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].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + stbi_uc x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + j->marker = stbi__skip_jpeg_junk_at_end(j); + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); + } else { + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); + } + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *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] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *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] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__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 stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *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] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + 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] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + 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] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *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; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + 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 = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("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 = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *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) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } 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 { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *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; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__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 >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// 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 + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__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 stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *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, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + 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]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (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 +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + int hit_zeof_once; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__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]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); + } + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end","Corrupt PNG"); + } + return 1; + } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (len > a->zout_end - zout) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + a->hit_zeof_once = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__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 stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// 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 + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub +}; + +static int stbi__paeth(int a, int b, int c) +{ + // This formulation looks very different from the reference in the PNG spec, but is + // actually equivalent and has favorable data dependencies and admits straightforward + // generation of branch-free code, which helps performance significantly. + int thresh = c*3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// adds an extra all-255 alpha channel +// dest == src is legal +// img_n must be 1 or 3 +static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) +{ + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + dest[i*2+1] = 255; + dest[i*2+0] = src[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i=x-1; i >= 0; --i) { + dest[i*4+3] = 255; + dest[i*4+2] = src[i*3+2]; + dest[i*4+1] = src[i*3+1]; + dest[i*4+0] = src[i*3+0]; + } + } +} + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16 ? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + stbi_uc *filter_buf; + int all_ok = 1; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + // Allocate two scan lines worth of filter workspace buffer. + filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filter_buf) return stbi__err("outofmem", "Out of memory"); + + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } + + for (j=0; j < y; ++j) { + // cur/prior filter buffers alternate + stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; + stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; + stbi_uc *dest = a->out + stride*j; + int nk = width * filter_bytes; + int filter = *raw++; + + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter","Corrupt PNG"); + break; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); + break; + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); + break; + } + + raw += nk; + + // expand decoded bits in cur to dest, also adding an extra alpha channel if desired + if (depth < 8) { + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + stbi_uc *in = cur; + stbi_uc *out = dest; + stbi_uc inb = 0; + stbi__uint32 nsmp = x*img_n; + + // expand bits to bytes first + if (depth == 4) { + for (i=0; i < nsmp; ++i) { + if ((i & 1) == 0) inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; + } + } else if (depth == 2) { + for (i=0; i < nsmp; ++i) { + if ((i & 3) == 0) inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; + } + } else { + STBI_ASSERT(depth == 1); + for (i=0; i < nsmp; ++i) { + if ((i & 7) == 0) inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; + } + } + + // insert alpha=255 values if desired + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x*img_n); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + stbi__uint16 *dest16 = (stbi__uint16*)dest; + stbi__uint32 nsmp = x*img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + STBI_ASSERT(img_n+1 == out_n); + if (img_n == 1) { + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; + } + } else { + STBI_ASSERT(img_n == 3); + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; + } + } + } + } + } + + STBI_FREE(filter_buf); + if (!all_ok) return 0; + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + 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) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_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 stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_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 : 65535); + 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 stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("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; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("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 stbi__err("too large", "Image too large to decode"); + } 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 stbi__err("too large","Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_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 (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && 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 (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) 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; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__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__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__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;/* >>= 1;*/ } + return n; +} + +static int stbi__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; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + 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 + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + 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 = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(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 index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // 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 * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_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 ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // 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; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 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 (stbi__get16be(s) != 3) + return stbi__errpuc("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.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("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 preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } 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 (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// 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/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__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 *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("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=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__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 = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *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; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + 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__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + 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 stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__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 + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("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; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__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 = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__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 *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__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; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // 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: + stbi__getn(s, rgbe, 4); + stbi__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 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__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) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__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 = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__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; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } + + if (req_comp && req_comp != s->img_n) { + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 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-stbi_uc 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.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' 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 - stbi__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 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ -- cgit v1.2.3 From a6f3b8c71513a1a7d416ec57627836ed4d9a7d94 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Wed, 25 Sep 2024 16:38:14 +0200 Subject: fully working map,textures and spritesheets togheter --- resource-manager/main.cpp | 7 +++++-- resource-manager/map_asset.cpp | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/resource-manager/main.cpp b/resource-manager/main.cpp index d0e8312..a301114 100644 --- a/resource-manager/main.cpp +++ b/resource-manager/main.cpp @@ -31,13 +31,11 @@ int main() { ResourceManager * rm = new ResourceManager(); - /* Texture* img = rm->Load("../img.png"); img->setTexture(*renderer); SpriteSheet* SS = rm->Load("../spritesheet_test.png"); SS->set_spritesheet_data(*renderer, 1, 4); - */ Map* map = rm->Load("../../asset/tiled/demo.tmx"); map->SetRenderer(*renderer); @@ -61,6 +59,11 @@ int main() { SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, img->getTexture(), NULL, NULL); + SS->select_sprite(sprite, 0); + SDL_Rect pos = {10,10,32,64}; + SS->draw_selected_sprite(renderer, &pos); + map->draw(); SDL_RenderPresent(renderer); } delete rm; diff --git a/resource-manager/map_asset.cpp b/resource-manager/map_asset.cpp index ab19e59..342edc3 100644 --- a/resource-manager/map_asset.cpp +++ b/resource-manager/map_asset.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,8 @@ void Map::SetMapTextures(){ const auto& tileSets = m_Map.getTilesets(); assert(~tileSets.empty()); + std::cout << "Processing SetMapTextures " << std::endl; + for (const auto& ts : tileSets ) { m_MapTextures.emplace_back(new TextureMap); if(!m_MapTextures.back()->loadFromFile(ts.getImagePath(), m_Renderer)){ @@ -60,6 +63,7 @@ void Map::SetMapTextures(){ void Map::SetMapLayers(){ const auto& mapLayers = m_Map.getLayers(); + std::cout << "Processing SetMapLayers " << std::endl; for(auto i = 0u; i < mapLayers.size(); ++i){ if (mapLayers[i]->getType() == tmx::Layer::Type::Tile) { m_RenderLayers.emplace_back(new MapLayer); -- cgit v1.2.3 From a08f5acf483d5371ccab70ba75c39ac6a5f96ce7 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Mon, 30 Sep 2024 10:56:13 +0200 Subject: changed resource manager based on feedback --- resource-manager/Audio_asset.cpp | 15 +++++++ resource-manager/Audio_asset.h | 2 +- resource-manager/CMakeLists.txt | 2 + resource-manager/Image_asset.cpp | 23 ++-------- resource-manager/Image_asset.h | 14 +------ resource-manager/main.cpp | 29 ++++++++----- resource-manager/map_asset.cpp | 65 +--------------------------- resource-manager/map_asset.h | 25 +---------- resource-manager/resource.h | 9 +++- resource-manager/resource_converter.cpp | 37 ++++++++++++++++ resource-manager/resource_converter.h | 24 +++++++++++ resource-manager/resource_fabricator.cpp | 22 ++++++++++ resource-manager/resource_fabricator.h | 10 +++-- resource-manager/resource_manager.cpp | 8 ---- resource-manager/resource_manager.h | 11 ----- resource-manager/spritesheet.cpp | 29 +------------ resource-manager/spritesheet.h | 17 +------- resource-manager/tiledMap.cpp | 72 ++++++++++++++++++++++++++++++++ resource-manager/tiledMap.h | 32 ++++++++++++++ 19 files changed, 248 insertions(+), 198 deletions(-) create mode 100644 resource-manager/resource_converter.cpp create mode 100644 resource-manager/resource_converter.h create mode 100644 resource-manager/tiledMap.cpp create mode 100644 resource-manager/tiledMap.h diff --git a/resource-manager/Audio_asset.cpp b/resource-manager/Audio_asset.cpp index e69de29..056034f 100644 --- a/resource-manager/Audio_asset.cpp +++ b/resource-manager/Audio_asset.cpp @@ -0,0 +1,15 @@ + + + + +#include "Audio_asset.h" +#include + + + +Audio::Audio(const std::string& content){ + this->m_content = content; +} + +Audio::~Audio(){ +} diff --git a/resource-manager/Audio_asset.h b/resource-manager/Audio_asset.h index 7f7f8cd..d25c52f 100644 --- a/resource-manager/Audio_asset.h +++ b/resource-manager/Audio_asset.h @@ -8,7 +8,7 @@ class Audio : public Resource { public: - Audio(const std::string& path); + Audio(const std::string&); ~Audio(); diff --git a/resource-manager/CMakeLists.txt b/resource-manager/CMakeLists.txt index cf883ae..bbee3dc 100644 --- a/resource-manager/CMakeLists.txt +++ b/resource-manager/CMakeLists.txt @@ -24,7 +24,9 @@ add_executable(main Audio_asset.cpp Image_asset.cpp resource_fabricator.cpp + tiledMap.cpp resource_manager.cpp + resource_converter.cpp spritesheet.cpp constants.cpp map_asset.cpp diff --git a/resource-manager/Image_asset.cpp b/resource-manager/Image_asset.cpp index c5599e9..0b3afad 100644 --- a/resource-manager/Image_asset.cpp +++ b/resource-manager/Image_asset.cpp @@ -3,31 +3,14 @@ #include "Image_asset.h" #include #include +#include -Texture::Texture(const std::string& path){ - m_surface = IMG_Load(path.c_str()); +Texture::Texture(const std::string& content){ + this->m_content = content; } -void Texture::setTexture(SDL_Renderer& renderer){ - m_texture = SDL_CreateTextureFromSurface(&renderer, m_surface); -} Texture::~Texture(){ - if (m_surface) { - SDL_FreeSurface(m_surface); - } - - if(m_texture){ - SDL_DestroyTexture(m_texture); - } -} - -SDL_Surface* Texture::getSurface() const { - return m_surface; -} - -SDL_Texture* Texture::getTexture() const{ - return m_texture; } diff --git a/resource-manager/Image_asset.h b/resource-manager/Image_asset.h index 93617e6..97daca1 100644 --- a/resource-manager/Image_asset.h +++ b/resource-manager/Image_asset.h @@ -3,26 +3,14 @@ #include "resource.h" -#include -#include #include - - class Texture : public Resource { public: - Texture(const std::string& path); + Texture(const std::string&); ~Texture(); - - void setTexture(SDL_Renderer& renderer); - SDL_Surface* getSurface() const; - SDL_Texture* getTexture() const; - -private: - SDL_Surface* m_surface; - SDL_Texture* m_texture; }; diff --git a/resource-manager/main.cpp b/resource-manager/main.cpp index a301114..6b3a23e 100644 --- a/resource-manager/main.cpp +++ b/resource-manager/main.cpp @@ -2,6 +2,7 @@ #include "Image_asset.h" #include "map_asset.h" +#include "resource_converter.h" #include "resource_manager.h" #include "spritesheet.h" #include @@ -14,6 +15,8 @@ #include #include #include +#include +#include int main() { SDL_Init(SDL_INIT_VIDEO); @@ -27,20 +30,25 @@ int main() { SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); + SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0); ResourceManager * rm = new ResourceManager(); + ResourceConverter resource_converter; Texture* img = rm->Load("../img.png"); - img->setTexture(*renderer); SpriteSheet* SS = rm->Load("../spritesheet_test.png"); - SS->set_spritesheet_data(*renderer, 1, 4); - Map* map = rm->Load("../../asset/tiled/demo.tmx"); - map->SetRenderer(*renderer); + + SDL_Surface* surface = resource_converter.FromStringToImage(*img); + SDL_Texture* m_texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_FreeSurface(surface); + TiledMap testingTiledMap = resource_converter.FromStringToMap(*map); + testingTiledMap.SetRenderer(*renderer); + SDL_SetRenderDrawColor(renderer, 168, 230, 255, 255); SDL_RenderClear(renderer); @@ -55,19 +63,20 @@ int main() { quit = true; break; } - } + } SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, img->getTexture(), NULL, NULL); - SS->select_sprite(sprite, 0); - SDL_Rect pos = {10,10,32,64}; - SS->draw_selected_sprite(renderer, &pos); - map->draw(); + + SDL_RenderCopy(renderer, m_texture, NULL, NULL); + testingTiledMap.draw(); + SDL_RenderPresent(renderer); } delete rm; + + SDL_DestroyTexture(m_texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); diff --git a/resource-manager/map_asset.cpp b/resource-manager/map_asset.cpp index 342edc3..bbabe2b 100644 --- a/resource-manager/map_asset.cpp +++ b/resource-manager/map_asset.cpp @@ -3,71 +3,10 @@ #include "map_asset.h" -#include "TextureMap.h" -#include "map_layer.h" -#include -#include -#include -#include -#include -#include -#include -#include - -Map::Map(const std::string& path){ - if(m_Map.load(path)){ - std::cout << "Map loaded correctly " << std::endl; - } +Map::Map(const std::string& content){ + this->m_content = content; } Map::~Map(){ - for(const auto& r : m_MapTextures){ - delete r; - } - m_MapTextures.clear(); - - for(const auto& m : m_RenderLayers){ - delete m; - } - m_RenderLayers.clear(); -} - - -void Map::SetRenderer(SDL_Renderer& renderer){ - this->m_Renderer = &renderer; - this->SetMapTextures(); - this->SetMapLayers(); -} - -void Map::draw() const{ - for(const auto& l : m_RenderLayers){ - l->draw(m_Renderer); - } -} - -void Map::SetMapTextures(){ - const auto& tileSets = m_Map.getTilesets(); - assert(~tileSets.empty()); - - std::cout << "Processing SetMapTextures " << std::endl; - - for (const auto& ts : tileSets ) { - m_MapTextures.emplace_back(new TextureMap); - if(!m_MapTextures.back()->loadFromFile(ts.getImagePath(), m_Renderer)){ - std::cerr << "Failed opening " << ts.getImagePath() << "\n"; - } - } -} - -void Map::SetMapLayers(){ - const auto& mapLayers = m_Map.getLayers(); - - std::cout << "Processing SetMapLayers " << std::endl; - for(auto i = 0u; i < mapLayers.size(); ++i){ - if (mapLayers[i]->getType() == tmx::Layer::Type::Tile) { - m_RenderLayers.emplace_back(new MapLayer); - m_RenderLayers.back()->create(this->m_Map, i, this->m_MapTextures); - } - } } diff --git a/resource-manager/map_asset.h b/resource-manager/map_asset.h index 39cb8f4..082d84d 100644 --- a/resource-manager/map_asset.h +++ b/resource-manager/map_asset.h @@ -1,35 +1,12 @@ #pragma once - -#include "TextureMap.h" -#include "map_layer.h" #include "resource.h" #include -#include -#include class Map : public Resource { public: - Map(const std::string& path); + Map(const std::string& ); ~Map(); - - void SetRenderer(SDL_Renderer&); - void draw() const; - - -private: - void SetMapTextures(); - void SetMapLayers(); - - -private: - tmx::Map m_Map; - std::vector m_MapTextures; - std::vector m_RenderLayers; - - SDL_Renderer* m_Renderer; - - }; diff --git a/resource-manager/resource.h b/resource-manager/resource.h index 6bc44ed..4f112e3 100644 --- a/resource-manager/resource.h +++ b/resource-manager/resource.h @@ -3,9 +3,16 @@ +#include class Resource{ public: - + virtual ~Resource() =default; + const std::string& getContent() const{ + return this->m_content; + } + +protected: + std::string m_content; }; diff --git a/resource-manager/resource_converter.cpp b/resource-manager/resource_converter.cpp new file mode 100644 index 0000000..63b7491 --- /dev/null +++ b/resource-manager/resource_converter.cpp @@ -0,0 +1,37 @@ + + +#include "resource_converter.h" +#include "Image_asset.h" +#include "map_asset.h" +#include "resource.h" +#include +#include +#include +#include + +ResourceConverter::ResourceConverter() { IMG_Init(IMG_INIT_PNG); } + +ResourceConverter::~ResourceConverter() { IMG_Quit(); } + +SDL_Surface * +ResourceConverter::FromStringToImage(const Texture& resource) { + const std::string& content = resource.getContent(); + SDL_RWops * rw = SDL_RWFromConstMem(content.data(), content.size()); + if (!rw) { + std::cerr << "Failed to create SDL_RWops: " << SDL_GetError() + << std::endl; + return nullptr; + } + + SDL_Surface * surface = IMG_Load_RW(rw, 1); + if (!surface) { + std::cerr << "Failed to load image: " << IMG_GetError() << std::endl; + } + return surface; +} + + +TiledMap ResourceConverter::FromStringToMap(const Map& resource){ + const std::string& content = resource.getContent(); + return TiledMap(content); +} diff --git a/resource-manager/resource_converter.h b/resource-manager/resource_converter.h new file mode 100644 index 0000000..d771163 --- /dev/null +++ b/resource-manager/resource_converter.h @@ -0,0 +1,24 @@ +#pragma once + + + +#include "Image_asset.h" +#include "map_asset.h" +#include "tiledMap.h" + +#include + +class ResourceConverter{ + + +public: + ResourceConverter(); + ~ResourceConverter(); + + SDL_Surface* FromStringToImage(const Texture&); + TiledMap FromStringToMap(const Map&); + + //TODO: + //convertrs a string map into a real map: + +}; diff --git a/resource-manager/resource_fabricator.cpp b/resource-manager/resource_fabricator.cpp index 36353e4..0633a40 100644 --- a/resource-manager/resource_fabricator.cpp +++ b/resource-manager/resource_fabricator.cpp @@ -1,4 +1,26 @@ #include "resource_fabricator.h" +#include +#include +#include +#include + + + +std::string ResourceFactory::convert_file_to_string(const std::string& path){ + std::ifstream file(path, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + std::cerr << "Failed to open file: " << path << std::endl; + return ""; + } + + std::ifstream::pos_type fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + + std::vector bytes(fileSize); + file.read(bytes.data(), fileSize); + + return std::string(bytes.begin(), bytes.end()); +} diff --git a/resource-manager/resource_fabricator.h b/resource-manager/resource_fabricator.h index 2d46b00..cb6929f 100644 --- a/resource-manager/resource_fabricator.h +++ b/resource-manager/resource_fabricator.h @@ -2,12 +2,10 @@ -#include "Audio_asset.h" -#include "Image_asset.h" #include "resource.h" #include "constants.h" -#include "spritesheet.h" #include +#include @@ -20,9 +18,13 @@ public: template static Resource* create_resource(const Constants::FILE_PATH& file_path){ - return new T(file_path); + + return new T(convert_file_to_string(file_path)); } +private: + static std::string convert_file_to_string(const std::string& path); + }; diff --git a/resource-manager/resource_manager.cpp b/resource-manager/resource_manager.cpp index 99918d1..1412592 100644 --- a/resource-manager/resource_manager.cpp +++ b/resource-manager/resource_manager.cpp @@ -3,14 +3,10 @@ #include "resource_manager.h" #include "constants.h" #include "resource.h" -#include "resource_fabricator.h" -#include -#include #include ResourceManager::ResourceManager(){ - IMG_Init(IMG_INIT_PNG); } ResourceManager::~ResourceManager(){ @@ -18,10 +14,6 @@ ResourceManager::~ResourceManager(){ for(auto pair : m_resources){ delete pair.second; } - - m_resources.clear(); - - IMG_Quit(); } diff --git a/resource-manager/resource_manager.h b/resource-manager/resource_manager.h index 5570313..ea9c80a 100644 --- a/resource-manager/resource_manager.h +++ b/resource-manager/resource_manager.h @@ -10,19 +10,8 @@ #include "constants.h" #include "resource_fabricator.h" - -enum class asset_type{ - TEXTURE, - SPRITESHEET, - AUDIO, - MAP, - UNKNOWN, -}; - using namespace crepe; - - class ResourceManager{ public: diff --git a/resource-manager/spritesheet.cpp b/resource-manager/spritesheet.cpp index b6ff60e..5ea4b65 100644 --- a/resource-manager/spritesheet.cpp +++ b/resource-manager/spritesheet.cpp @@ -1,40 +1,15 @@ #include "spritesheet.h" -#include "Image_asset.h" -#include -#include -#include -#include -#include #include -SpriteSheet::SpriteSheet(const std::string& path){ - m_spritesheet = new Texture(path); -} - -void SpriteSheet::set_spritesheet_data(SDL_Renderer& renderer, const int row, const int column){ - m_spritesheet->setTexture(renderer); - m_clip.w = m_spritesheet->getSurface()->w / column; - m_clip.h = m_spritesheet->getSurface()->h / row; +SpriteSheet::SpriteSheet(const std::string& content){ + this->m_content = content; } SpriteSheet::~SpriteSheet(){ - delete m_spritesheet; } -void SpriteSheet::select_sprite(const int x, const int y){ - m_clip.x = x * m_clip.w; - m_clip.y = y * m_clip.h; -} -void SpriteSheet::draw_selected_sprite(SDL_Renderer* window_surface, SDL_Rect* position){ - SDL_RenderCopy(window_surface, m_spritesheet->getTexture(), &m_clip, position); -} - - -SDL_Surface* SpriteSheet::getSurface() const { - return m_spritesheet->getSurface(); -} diff --git a/resource-manager/spritesheet.h b/resource-manager/spritesheet.h index be1a051..9b222e7 100644 --- a/resource-manager/spritesheet.h +++ b/resource-manager/spritesheet.h @@ -3,29 +3,14 @@ -#include "Image_asset.h" #include "resource.h" -#include -#include -#include #include class SpriteSheet : public Resource{ - public: - SpriteSheet(const std::string& path); + SpriteSheet(const std::string&); ~SpriteSheet(); - void set_spritesheet_data(SDL_Renderer& renderer, const int row, const int column); - - - void select_sprite(const int x, const int y); - void draw_selected_sprite(SDL_Renderer* window_surface, SDL_Rect* position); - SDL_Surface* getSurface() const; - -private: - SDL_Rect m_clip; - Texture* m_spritesheet; }; diff --git a/resource-manager/tiledMap.cpp b/resource-manager/tiledMap.cpp new file mode 100644 index 0000000..659c1fa --- /dev/null +++ b/resource-manager/tiledMap.cpp @@ -0,0 +1,72 @@ + + + + +#include "tiledMap.h" +#include "TextureMap.h" +#include "map_layer.h" +#include +#include +#include +#include +#include +#include +#include + + +TiledMap::TiledMap(const std::string& content){ + if(m_TmxMap.loadFromString(content, "../../asset/tiled/")){ + std::cout << "Map loaded correctly " << std::endl; + } +} + +TiledMap::~TiledMap(){ + for(const auto& r : m_MapTextures){ + delete r; + } + m_MapTextures.clear(); + + for(const auto& m : m_RenderLayers){ + delete m; + } + m_RenderLayers.clear(); +} + + +void TiledMap::SetRenderer(SDL_Renderer& renderer){ + this->m_Renderer = &renderer; + this->SetMapTextures(); + this->SetMapLayers(); +} + +void TiledMap::draw() const{ + for(const auto& l : m_RenderLayers){ + l->draw(m_Renderer); + } +} + +void TiledMap::SetMapTextures(){ + const auto& tileSets = m_TmxMap.getTilesets(); + assert(~tileSets.empty()); + + std::cout << "Processing SetMapTextures " << std::endl; + + for (const auto& ts : tileSets ) { + m_MapTextures.emplace_back(new TextureMap); + if(!m_MapTextures.back()->loadFromFile(ts.getImagePath(), m_Renderer)){ + std::cerr << "Failed opening " << ts.getImagePath() << "\n"; + } + } +} + +void TiledMap::SetMapLayers(){ + const auto& mapLayers = m_TmxMap.getLayers(); + + std::cout << "Processing SetMapLayers " << std::endl; + for(auto i = 0u; i < mapLayers.size(); ++i){ + if (mapLayers[i]->getType() == tmx::Layer::Type::Tile) { + m_RenderLayers.emplace_back(new MapLayer); + m_RenderLayers.back()->create(this->m_TmxMap, i, this->m_MapTextures); + } + } +} diff --git a/resource-manager/tiledMap.h b/resource-manager/tiledMap.h new file mode 100644 index 0000000..be5b6a6 --- /dev/null +++ b/resource-manager/tiledMap.h @@ -0,0 +1,32 @@ + + +#include "TextureMap.h" +#include "map_layer.h" +#include +#include +#include + +class TiledMap { + +public: + TiledMap(const std::string&); + ~TiledMap(); + + void SetRenderer(SDL_Renderer&); + void draw() const; + + +private: + void SetMapTextures(); + void SetMapLayers(); + + +private: + tmx::Map m_TmxMap; + std::vector m_MapTextures; + std::vector m_RenderLayers; + + SDL_Renderer* m_Renderer; + + +}; -- cgit v1.2.3 From 2d13805218eb34b6e06205c1b65c341ebcdad504 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Tue, 1 Oct 2024 11:03:50 +0200 Subject: changed resource manager to singleton --- asset/tiled/demo.tmx | 21 +++++++ asset/tiled/images/tilemap/platform.tsx | 18 ++++++ asset/tiled/images/tilemap/tileset.png | Bin 0 -> 29737 bytes asset/tiled/images/tilemap/tileset.txt | 2 + asset/tiled/images/tilemap/tileset02.png | Bin 0 -> 6478 bytes asset/tiled/platform.tmx | 94 +++++++++++++++++++++++++++++++ resource-manager/main.cpp | 6 +- resource-manager/resource_manager.cpp | 11 ++++ resource-manager/resource_manager.h | 24 ++++++-- 9 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 asset/tiled/demo.tmx create mode 100644 asset/tiled/images/tilemap/platform.tsx create mode 100644 asset/tiled/images/tilemap/tileset.png create mode 100644 asset/tiled/images/tilemap/tileset.txt create mode 100644 asset/tiled/images/tilemap/tileset02.png create mode 100644 asset/tiled/platform.tmx diff --git a/asset/tiled/demo.tmx b/asset/tiled/demo.tmx new file mode 100644 index 0000000..8183148 --- /dev/null +++ b/asset/tiled/demo.tmx @@ -0,0 +1,21 @@ + + + + + + + + eJxjYBj6gBeIeZAwH5H6OIFYBQvmooEbBwoAAO2gAQc= + + + + + eJxjYBhcgIUCvcxIbH5KHTJMAAAZ1AAX + + + + + eJxjYBjaQBCIBaCYG4k9CiAAAGXcAF0= + + + diff --git a/asset/tiled/images/tilemap/platform.tsx b/asset/tiled/images/tilemap/platform.tsx new file mode 100644 index 0000000..6a48b12 --- /dev/null +++ b/asset/tiled/images/tilemap/platform.tsx @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/asset/tiled/images/tilemap/tileset.png b/asset/tiled/images/tilemap/tileset.png new file mode 100644 index 0000000..f13825d Binary files /dev/null and b/asset/tiled/images/tilemap/tileset.png differ diff --git a/asset/tiled/images/tilemap/tileset.txt b/asset/tiled/images/tilemap/tileset.txt new file mode 100644 index 0000000..1676dad --- /dev/null +++ b/asset/tiled/images/tilemap/tileset.txt @@ -0,0 +1,2 @@ +tileset by Alucard +http://opengameart.org/content/pixel-art-platformer-complete-pack \ No newline at end of file diff --git a/asset/tiled/images/tilemap/tileset02.png b/asset/tiled/images/tilemap/tileset02.png new file mode 100644 index 0000000..9f594e4 Binary files /dev/null and b/asset/tiled/images/tilemap/tileset02.png differ diff --git a/asset/tiled/platform.tmx b/asset/tiled/platform.tmx new file mode 100644 index 0000000..2b40b85 --- /dev/null +++ b/asset/tiled/platform.tmx @@ -0,0 +1,94 @@ + + + + + + + + + + + + eJzt2VsKwyAQBdCsoo8VNN3/BvvbQC1Em9iZOQeE/AQcyFXjLAuc69YxIKuePMgLVckCtNkz4LvrjgGVXHYMqGZPPuSFSuQCPrt3Dshudt/jOfAuZLfOngD8MfmANvkY43ya22P2BIKzvkCbfECbfECb8ykAAABnqNb/qlYvY6rdX0avV77PVe3+Mnq90fMNv/a+Z8gHbK2NZ2B7Jox+PqQO/8rQZi0HAAAAiCf7nWf2+jhW9v5u9vo41sj3E2Ftlg9GjPTMIvTbIswRYKYXswgSvA== + + + + + + + + eJztm9tOw0AMBc0DPAEfAKIY6P9/I6kgqA25NInXa3dnpCOeqJxmZ+89CgCMcfwNAPwHPwCmwQ+AafDDlrvaBYAp+GHLZ5fDIF9VK4I94AfU4qXL60LeqlX3A34ATIMfvrA+yQV++MC6JCf4AdkpOSbjB2RmbFy2HJvxw5bh+2IOlRv8iA9rehueujwO8rzwP/gRk6l5A+PSNua+z7nvEj/KsqXPuhW2jHvRxkr8KMfWPmsN0dqTdnkvlA+/x/ijZT/G+vYM/XuksxSVyzZ8LBxvT1r1g/n9PlR8fJiKlyOt+gHbUKnrhbcj+AFLqPjNn6LNt/AjLyXniCpxnfAcS/ADelRyOeHhCH7ACZX67dvSkdO+99T+5FzO9y7vzz63BaKdFURBpX67tozVecv5Z94Kpzl5/3yHkb/s3V6iUr89Zwi0h0r9dpclUI+18+S58/1r5pAqedfg+NEOKuXuKl07ryb4sYZr+nKL+1kq9d85wY81LJ23WZy9qdCHZ0x2ou/dquBF5mRGJeZvB3pU6r9f0p4fKuv6ZG9H1tZH4iYTKtvbnYcje+ojMZMFlf3PWtIRi/pIvERHxbZPLuGIGtZHYiUiKmXPtKzW7X2dtd8hKZcH2XYf2CrDMzd1fPY9jnjWSeqmxl2HKHcgstRJ/PMNRu5JzA== + + + + + eJzt2+FqwjAUBtDihO2fbnPv/6rbYGUxNOuqbW9izoEgIsqF+jVpczsMAAAAAACkPqILgIq9RxdA1U7RBQSTDyiTjz4dowtoxJr5eP0a52T0PjfX7PDzuuUxajmDY+3mD7byFF3AHcba5YN7nSfGt0PxG/WLzke6FnsLqgFKovNBXVwrXhvnvsswPT9ODed5lvC/YU/5+t45HwCgX+la0HUJsIaWexBga3M9CM+7VAH1SNedf/Ug9JCNUm8G9Mxzm1CmrwfKWsrHXN/RlP/s7S/9TfqR58N9PPiV5+PWZ4n0YPGI1soHPKJL9r7lZ+0AAAAgZQ+Bnr3MfO4eKb3LM5Lujy29Rzq3t3ZLX8VeTkPd9QHU4hO/Kwa1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resource-manager/main.cpp b/resource-manager/main.cpp index 6b3a23e..0148555 100644 --- a/resource-manager/main.cpp +++ b/resource-manager/main.cpp @@ -15,8 +15,6 @@ #include #include #include -#include -#include int main() { SDL_Init(SDL_INIT_VIDEO); @@ -32,8 +30,8 @@ int main() { SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0); - - ResourceManager * rm = new ResourceManager(); + + ResourceManager* rm = ResourceManager::get_instance(); ResourceConverter resource_converter; Texture* img = rm->Load("../img.png"); diff --git a/resource-manager/resource_manager.cpp b/resource-manager/resource_manager.cpp index 1412592..aee0ce2 100644 --- a/resource-manager/resource_manager.cpp +++ b/resource-manager/resource_manager.cpp @@ -5,6 +5,15 @@ #include "resource.h" #include +ResourceManager* ResourceManager::m_single_resource_manager = nullptr; + +ResourceManager* ResourceManager::get_instance(){ + if (m_single_resource_manager == nullptr) { + m_single_resource_manager = new ResourceManager(); + } + return m_single_resource_manager; +} + ResourceManager::ResourceManager(){ } @@ -14,6 +23,8 @@ ResourceManager::~ResourceManager(){ for(auto pair : m_resources){ delete pair.second; } + + delete m_single_resource_manager; } diff --git a/resource-manager/resource_manager.h b/resource-manager/resource_manager.h index ea9c80a..c155559 100644 --- a/resource-manager/resource_manager.h +++ b/resource-manager/resource_manager.h @@ -14,11 +14,29 @@ using namespace crepe; class ResourceManager{ -public: +private: + + static ResourceManager* m_single_resource_manager; + std::unordered_map m_resources; + + + +protected: ResourceManager(); ~ResourceManager(); - + +public: + ResourceManager(const ResourceManager &) = delete; + ResourceManager(ResourceManager &&) = delete; + ResourceManager &operator=(const ResourceManager &) = delete; + ResourceManager &operator=(ResourceManager &&) = delete; + + static ResourceManager* get_instance(); + + + +public: template T* Load(const Constants::FILE_PATH& file_path){ @@ -35,8 +53,6 @@ public: void Unload(const Constants::FILE_PATH& file_path); -private: - std::unordered_map m_resources; }; -- cgit v1.2.3 From 4628638f952f882e494788ca1cf880a029eba5ab Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Tue, 1 Oct 2024 13:40:56 +0200 Subject: first try at events --- .vscode/settings.json | 66 +++++++++++- .vscode/tasks.json | 28 +++++ mwe/events/.vscode/settings.json | 54 ++++++++++ mwe/events/CMakeLists.txt | 25 +++++ mwe/events/imgs/demo.bmp | Bin 0 -> 7998698 bytes mwe/events/imgs/demo.jpg | Bin 0 -> 903706 bytes mwe/events/include/event.h | 207 ++++++++++++++++++++++++++++++++++++ mwe/events/include/eventHandler.h | 39 +++++++ mwe/events/include/window.h | 19 ++++ mwe/events/src/eventManager.cpp | 1 + mwe/events/src/main.cpp | 14 +++ mwe/events/src/window.cpp | 33 ++++++ mwe/events/versions/delayBased.cpp | 56 ++++++++++ mwe/gameloop/include/eventManager.h | 6 +- 14 files changed, 546 insertions(+), 2 deletions(-) create mode 100644 .vscode/tasks.json create mode 100644 mwe/events/.vscode/settings.json create mode 100644 mwe/events/CMakeLists.txt create mode 100644 mwe/events/imgs/demo.bmp create mode 100644 mwe/events/imgs/demo.jpg create mode 100644 mwe/events/include/event.h create mode 100644 mwe/events/include/eventHandler.h create mode 100644 mwe/events/include/window.h create mode 100644 mwe/events/src/eventManager.cpp create mode 100644 mwe/events/src/main.cpp create mode 100644 mwe/events/src/window.cpp create mode 100644 mwe/events/versions/delayBased.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index d265510..936b057 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,67 @@ { - "cmake.sourceDirectory": "${workspaceFolder}/src" + "cmake.sourceDirectory": "${workspaceFolder}/src", + "files.associations": { + "functional": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "list": "cpp", + "map": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "format": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "semaphore": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp" + } } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..05054c5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: g++ build active file", + "command": "/usr/bin/g++", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/mwe/events/.vscode/settings.json b/mwe/events/.vscode/settings.json new file mode 100644 index 0000000..b2c5be7 --- /dev/null +++ b/mwe/events/.vscode/settings.json @@ -0,0 +1,54 @@ +{ + "files.associations": { + "iostream": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "list": "cpp", + "map": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "valarray": "cpp" + } +} diff --git a/mwe/events/CMakeLists.txt b/mwe/events/CMakeLists.txt new file mode 100644 index 0000000..3bbda37 --- /dev/null +++ b/mwe/events/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.5) +project(gameloop) + +# Set the C++ standard (optional, but good practice) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) +set(CMAKE_BUILD_TYPE Debug) + +# Find the SDL2 package +find_package(SDL2 REQUIRED) + +add_executable(gameloop + src/window.cpp + src/main.cpp + src/eventManager.cpp +) + +# Link the SDL2 library to your project +target_link_libraries(gameloop ${SDL2_LIBRARIES}) + +# Include SDL2 header files and project headers +target_include_directories(gameloop PRIVATE ${SDL2_INCLUDE_DIRS}) +target_include_directories(gameloop PRIVATE ${CMAKE_SOURCE_DIR}/include) + diff --git a/mwe/events/imgs/demo.bmp b/mwe/events/imgs/demo.bmp new file mode 100644 index 0000000..65354fe Binary files /dev/null and b/mwe/events/imgs/demo.bmp differ diff --git a/mwe/events/imgs/demo.jpg b/mwe/events/imgs/demo.jpg new file mode 100644 index 0000000..f534e1b Binary files /dev/null and b/mwe/events/imgs/demo.jpg differ diff --git a/mwe/events/include/event.h b/mwe/events/include/event.h new file mode 100644 index 0000000..8774a7d --- /dev/null +++ b/mwe/events/include/event.h @@ -0,0 +1,207 @@ +#pragma once +#include +#include +#include +#include +using Keycode = uint16_t; +enum : Keycode { + // From glfw3.h + Space = 32, + Apostrophe = 39, /* ' */ + Comma = 44, /* , */ + Minus = 45, /* - */ + Period = 46, /* . */ + Slash = 47, /* / */ + + D0 = 48, /* 0 */ + D1 = 49, /* 1 */ + D2 = 50, /* 2 */ + D3 = 51, /* 3 */ + D4 = 52, /* 4 */ + D5 = 53, /* 5 */ + D6 = 54, /* 6 */ + D7 = 55, /* 7 */ + D8 = 56, /* 8 */ + D9 = 57, /* 9 */ + + Semicolon = 59, /* ; */ + Equal = 61, /* = */ + + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + + LeftBracket = 91, /* [ */ + Backslash = 92, /* \ */ + RightBracket = 93, /* ] */ + GraveAccent = 96, /* ` */ + + World1 = 161, /* non-US #1 */ + World2 = 162, /* non-US #2 */ + + /* Function keys */ + Escape = 256, + Enter = 257, + Tab = 258, + Backspace = 259, + Insert = 260, + Delete = 261, + Right = 262, + Left = 263, + Down = 264, + Up = 265, + PageUp = 266, + PageDown = 267, + Home = 268, + End = 269, + CapsLock = 280, + ScrollLock = 281, + NumLock = 282, + PrintScreen = 283, + Pause = 284, + F1 = 290, + F2 = 291, + F3 = 292, + F4 = 293, + F5 = 294, + F6 = 295, + F7 = 296, + F8 = 297, + F9 = 298, + F10 = 299, + F11 = 300, + F12 = 301, + F13 = 302, + F14 = 303, + F15 = 304, + F16 = 305, + F17 = 306, + F18 = 307, + F19 = 308, + F20 = 309, + F21 = 310, + F22 = 311, + F23 = 312, + F24 = 313, + F25 = 314, + + /* Keypad */ + KP0 = 320, + KP1 = 321, + KP2 = 322, + KP3 = 323, + KP4 = 324, + KP5 = 325, + KP6 = 326, + KP7 = 327, + KP8 = 328, + KP9 = 329, + KPDecimal = 330, + KPDivide = 331, + KPMultiply = 332, + KPSubtract = 333, + KPAdd = 334, + KPEnter = 335, + KPEqual = 336, + + LeftShift = 340, + LeftControl = 341, + LeftAlt = 342, + LeftSuper = 343, + RightShift = 344, + RightControl = 345, + RightAlt = 346, + RightSuper = 347, + Menu = 348 +}; + +class UUIDGenerator { +public: + static std::uint32_t GetUniqueID() + { + static std::uint32_t id = 0; + return ++id; + } +}; +class Event { +public: + virtual ~Event() = default; + virtual std::uint32_t GetEventType() const = 0; + + virtual std::string ToString() const { + return std::to_string(GetEventType()); + }; + + bool isHandled { false }; +}; +#define EVENT_TYPE(event_type) \ + static std::uint32_t GetStaticEventType() \ + { \ + static std::uint32_t type = UUIDGenerator::GetUniqueID(); \ + return type; \ + } \ + std::uint32_t GetEventType() const override \ + { \ + return GetStaticEventType(); \ + } + +class KeyPressedEvent : public Event { +public: + EVENT_TYPE("KeyPressedEvent"); + + KeyPressedEvent(int keyCode, int repeatCount) + : key(keyCode) + , repeatCount(repeatCount) + { + } + std::string ToString() const override + { + return "KeyPressedEvent KeyPressed" + std::to_string(key); + } + +public: + Keycode key { 0 }; + int repeatCount { 0 }; +}; + +class KeyReleasedEvent : public Event { +public: + EVENT_TYPE("KeyReleased"); + + KeyReleasedEvent(int keyCode) + : key(keyCode) + { + } + std::string ToString() const override + { + return "KeyPressedEvent KeyPressed" + std::to_string(key); + } + +public: + Keycode key { 0 }; +}; + + diff --git a/mwe/events/include/eventHandler.h b/mwe/events/include/eventHandler.h new file mode 100644 index 0000000..c17c586 --- /dev/null +++ b/mwe/events/include/eventHandler.h @@ -0,0 +1,39 @@ +#pragma once +#include "event.h" +#include +#include +template +using EventHandler = std::function; +class EventHandlerWrapperInterface { +public: + void Exec(const Event& e) + { + Call(e); + } + + virtual std::string GetType() const = 0; + +private: + virtual void Call(const Event& e) = 0; +}; + +template +class EventHandlerWrapper : public EventHandlerWrapperInterface { +public: + explicit EventHandlerWrapper(const EventHandler& handler) + : m_handler(handler) + , m_handlerType(m_handler.target_type().name()) {}; + +private: + void Call(const Event& e) override + { + if (e.GetEventType() == EventType::GetStaticEventType()) { + m_handler(static_cast(e)); + } + } + + std::string GetType() const override { return m_handlerType; } + + EventHandler m_handler; + const std::string m_handlerType; +}; diff --git a/mwe/events/include/window.h b/mwe/events/include/window.h new file mode 100644 index 0000000..9020b1a --- /dev/null +++ b/mwe/events/include/window.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include +#include +class WindowManager { +public: + WindowManager(); + virtual ~WindowManager(); + bool initWindow(); + void destroyWindow(); + + SDL_Renderer * getRenderer(); + +private: + const int SCREEN_WIDTH = 800; + const int SCREEN_HEIGHT = 600; + SDL_Window * window = NULL; + SDL_Renderer * renderer = NULL; +}; diff --git a/mwe/events/src/eventManager.cpp b/mwe/events/src/eventManager.cpp new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/mwe/events/src/eventManager.cpp @@ -0,0 +1 @@ + diff --git a/mwe/events/src/main.cpp b/mwe/events/src/main.cpp new file mode 100644 index 0000000..8d8567d --- /dev/null +++ b/mwe/events/src/main.cpp @@ -0,0 +1,14 @@ +#include +#include +#include "event.h" + + +int main(int argc, char * args[]) { + + KeyPressedEvent keyEvent(1,1); + KeyReleasedEvent keyRelease(1); + std::cout << keyEvent.GetEventType() << std::endl; + std::cout << keyRelease.GetEventType() << std::endl; + std::cin; + return 0; +} diff --git a/mwe/events/src/window.cpp b/mwe/events/src/window.cpp new file mode 100644 index 0000000..61a4105 --- /dev/null +++ b/mwe/events/src/window.cpp @@ -0,0 +1,33 @@ +#include "window.h" +WindowManager::WindowManager() { + if (!initWindow()) { + printf("Failed to initialize!\n"); + } +} +WindowManager::~WindowManager() { destroyWindow(); } +SDL_Renderer * WindowManager::getRenderer() { return renderer; } + +bool WindowManager::initWindow() { + if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { + fprintf(stderr, "Error inititalising SDL.\n"); + return false; + } + window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, + SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + if (!window) { + fprintf(stderr, "Error creating SDL Window. \n"); + return false; + } + renderer = SDL_CreateRenderer(window, -1, 0); + if (!renderer) { + fprintf(stderr, "Error creating SDL renderer. \n"); + return false; + } + return true; +} +void WindowManager::destroyWindow() { + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} diff --git a/mwe/events/versions/delayBased.cpp b/mwe/events/versions/delayBased.cpp new file mode 100644 index 0000000..253a03a --- /dev/null +++ b/mwe/events/versions/delayBased.cpp @@ -0,0 +1,56 @@ +#include "loopManager.h" +#include "timer.h" +LoopManager::LoopManager() {} +void LoopManager::processInput() { + SDL_Event event; + SDL_PollEvent(&event); + switch (event.type) { + case SDL_QUIT: + gameRunning = false; + break; + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_ESCAPE) { + gameRunning = false; + } + break; + } +} +void LoopManager::loop() { + fprintf(stderr, "loop. \n"); + while (gameRunning) { + //Timer::getInstance().update(); + //deltaTime = Timer::getInstance().getDeltaTime(); + processInput(); + update(); + render(); + } + window.destroyWindow(); +} +void LoopManager::setup() { + gameRunning = window.initWindow(); + LoopTimer::getInstance().start(); + LoopTimer::getInstance().setFPS(210); + + for (int i = 0; i < 2; i++) { + GameObject * square2 + = new GameObject("square2", i * 40, i * 40, 20, 20, 0, 0); + objectList.push_back(square2); + } +} +void LoopManager::render() { + if (gameRunning) { + window.render(objectList); + } +} + +void LoopManager::update() { + LoopTimer & timer = LoopTimer::getInstance(); + timer.enforceFrameRate(); + timer.update(); + float delta = timer.getDeltaTime(); + + for (int i = 0; i < objectList.size(); i++) { + objectList[i]->setX(objectList[i]->getX() + 50 * delta); + objectList[i]->setY(objectList[i]->getY() + 50 * delta); + } +} diff --git a/mwe/gameloop/include/eventManager.h b/mwe/gameloop/include/eventManager.h index 69c6801..ba1ca32 100644 --- a/mwe/gameloop/include/eventManager.h +++ b/mwe/gameloop/include/eventManager.h @@ -1 +1,5 @@ -class EventManager {}; +#pragma once +class EventManager { + public: + EventManager(); +}; -- cgit v1.2.3 From bd24f87e18bef29679023ee9a60028f2afdbb470 Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Tue, 1 Oct 2024 19:10:24 +0200 Subject: particle POC --- src/CMakeLists.txt | 7 +- src/crepe/CMakeLists.txt | 6 +- src/crepe/Particle.cpp | 14 +++ src/crepe/Particle.hpp | 19 +++ src/crepe/ParticleEmitter.cpp | 40 ++++++ src/crepe/ParticleEmitter.hpp | 28 +++++ .../CMakeFiles/CMakeDirectoryInformation.cmake | 16 +++ src/crepe/crepe/CMakeFiles/progress.marks | 1 + src/crepe/crepe/Makefile | 140 +++++++++++++++++++++ src/crepe/crepe/cmake_install.cmake | 44 +++++++ src/crepe/main.cpp | 84 ++++++++++++- 11 files changed, 394 insertions(+), 5 deletions(-) create mode 100644 src/crepe/Particle.cpp create mode 100644 src/crepe/Particle.hpp create mode 100644 src/crepe/ParticleEmitter.cpp create mode 100644 src/crepe/ParticleEmitter.hpp create mode 100644 src/crepe/crepe/CMakeFiles/CMakeDirectoryInformation.cmake create mode 100644 src/crepe/crepe/CMakeFiles/progress.marks create mode 100644 src/crepe/crepe/Makefile create mode 100644 src/crepe/crepe/cmake_install.cmake diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0090188..b8ed359 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,13 +4,16 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) -# enable debug features set(CMAKE_BUILD_TYPE Debug) add_compile_definitions(DEBUG) project(crepe C CXX) +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIRS}) + add_executable(main) -add_subdirectory(crepe) +target_link_libraries(main ${SDL2_LIBRARIES}) +add_subdirectory(crepe) \ No newline at end of file diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 392b7d7..c825529 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -1,3 +1,7 @@ target_sources(main PUBLIC - main.cpp + main.cpp + Particle.cpp + ParticleEmitter.cpp ) + +target_include_directories(main PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp new file mode 100644 index 0000000..90957db --- /dev/null +++ b/src/crepe/Particle.cpp @@ -0,0 +1,14 @@ +#include "Particle.hpp" + +Particle::Particle(float lifespan, Position position, Position velocity) + : lifespan(lifespan), position(position), velocity(velocity), timeInLife(0.0f) {} + +void Particle::update(float deltaTime) { + timeInLife += deltaTime; + position.x += static_cast(velocity.x * deltaTime); + position.y += static_cast(velocity.y * deltaTime); +} + +bool Particle::isAlive() const { + return timeInLife < lifespan; +} diff --git a/src/crepe/Particle.hpp b/src/crepe/Particle.hpp new file mode 100644 index 0000000..f8d2770 --- /dev/null +++ b/src/crepe/Particle.hpp @@ -0,0 +1,19 @@ +#pragma once + +class Particle { +public: + struct Position { + int x; + int y; + }; + + Position position; + Position velocity; + float lifespan; + + Particle(float lifespan, Position position, Position velocity); + void update(float deltaTime); + bool isAlive() const; +private: + float timeInLife; +}; diff --git a/src/crepe/ParticleEmitter.cpp b/src/crepe/ParticleEmitter.cpp new file mode 100644 index 0000000..13aaeae --- /dev/null +++ b/src/crepe/ParticleEmitter.cpp @@ -0,0 +1,40 @@ +#include "ParticleEmitter.hpp" + +ParticleEmitter::ParticleEmitter(int maxParticles, float emissionRate) + : maxParticles(maxParticles), emissionRate(emissionRate), elapsedTime(0.0f) { + position = { 0, 0 }; + std::srand(static_cast(std::time(nullptr))); / +} + +void ParticleEmitter::update(float deltaTime) { + elapsedTime += deltaTime; + + while (elapsedTime >= (1.0f / emissionRate) && particles.size() < maxParticles) { + elapsedTime -= (1.0f / emissionRate); + emitParticle(); + } + + for (auto it = particles.begin(); it != particles.end();) { + it->update(deltaTime); + if (!it->isAlive()) { + it = particles.erase(it); + } else { + ++it; + } + } +} + +void ParticleEmitter::emitParticle() { + Particle::Position initialPosition = { position.x, position.y }; + Particle::Position initialVelocity = { + static_cast(randFloat(-50.0f, 50.0f, 10.0f, 100.0f)), + static_cast(randFloat(-50.0f, 50.0f, 10.0f, 100.0f)) + }; + particles.emplace_back(2.0f, initialPosition, initialVelocity); +} + +float ParticleEmitter::randFloat(float minangle, float maxangle, float minVel, float maxVel) { + float angle = static_cast(rand()) / RAND_MAX * (maxangle - minangle) + minangle; + float velocity = static_cast(rand()) / RAND_MAX * (maxVel - minVel) + minVel; + return velocity * std::cos(angle); +} diff --git a/src/crepe/ParticleEmitter.hpp b/src/crepe/ParticleEmitter.hpp new file mode 100644 index 0000000..682a2ae --- /dev/null +++ b/src/crepe/ParticleEmitter.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include "Particle.hpp" +#include + +class ParticleEmitter { +public: + std::vector particles; + + struct Position { + int x; + int y; + } position; + + int maxParticles; + float emissionRate; + float elapsedTime; + + ParticleEmitter(int maxParticles, float emissionRate); + void update(float deltaTime); + +private: + void emitParticle(); + float randFloat(float minangle, float maxangle, float minVel, float maxVel); +}; diff --git a/src/crepe/crepe/CMakeFiles/CMakeDirectoryInformation.cmake b/src/crepe/crepe/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..8d03506 --- /dev/null +++ b/src/crepe/crepe/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.28 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/jaro/crepe/src") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/jaro/crepe/src/crepe") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/src/crepe/crepe/CMakeFiles/progress.marks b/src/crepe/crepe/CMakeFiles/progress.marks new file mode 100644 index 0000000..573541a --- /dev/null +++ b/src/crepe/crepe/CMakeFiles/progress.marks @@ -0,0 +1 @@ +0 diff --git a/src/crepe/crepe/Makefile b/src/crepe/crepe/Makefile new file mode 100644 index 0000000..094215a --- /dev/null +++ b/src/crepe/crepe/Makefile @@ -0,0 +1,140 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.28 + +# Default target executed when no arguments are given to make. +default_target: all +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + +# Disable VCS-based implicit rules. +% : %,v + +# Disable VCS-based implicit rules. +% : RCS/% + +# Disable VCS-based implicit rules. +% : RCS/%,v + +# Disable VCS-based implicit rules. +% : SCCS/s.% + +# Disable VCS-based implicit rules. +% : s.% + +.SUFFIXES: .hpux_make_needs_suffix_list + +# Command-line flag to silence nested $(MAKE). +$(VERBOSE)MAKESILENT = -s + +#Suppress display of executed commands. +$(VERBOSE).SILENT: + +# A target that is always out of date. +cmake_force: +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E rm -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/jaro/crepe/src + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/jaro/crepe/src/crepe + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache +.PHONY : edit_cache/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake --regenerate-during-build -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache +.PHONY : rebuild_cache/fast + +# The main all target +all: cmake_check_build_system + cd /home/jaro/crepe/src/crepe && $(CMAKE_COMMAND) -E cmake_progress_start /home/jaro/crepe/src/crepe/CMakeFiles /home/jaro/crepe/src/crepe/crepe//CMakeFiles/progress.marks + cd /home/jaro/crepe/src/crepe && $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 crepe/all + $(CMAKE_COMMAND) -E cmake_progress_start /home/jaro/crepe/src/crepe/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + cd /home/jaro/crepe/src/crepe && $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 crepe/clean +.PHONY : clean + +# The main clean target +clean/fast: clean +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /home/jaro/crepe/src/crepe && $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 crepe/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /home/jaro/crepe/src/crepe && $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 crepe/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /home/jaro/crepe/src/crepe && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... edit_cache" + @echo "... rebuild_cache" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /home/jaro/crepe/src/crepe && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/src/crepe/crepe/cmake_install.cmake b/src/crepe/crepe/cmake_install.cmake new file mode 100644 index 0000000..cd57803 --- /dev/null +++ b/src/crepe/crepe/cmake_install.cmake @@ -0,0 +1,44 @@ +# Install script for directory: /home/jaro/crepe/src/crepe + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "Debug") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +# Set default install directory permissions. +if(NOT DEFINED CMAKE_OBJDUMP) + set(CMAKE_OBJDUMP "/usr/bin/objdump") +endif() + diff --git a/src/crepe/main.cpp b/src/crepe/main.cpp index 8e9a184..73d041c 100644 --- a/src/crepe/main.cpp +++ b/src/crepe/main.cpp @@ -1,3 +1,83 @@ -#include +#include +#include +#include +#include +#include "ParticleEmitter.hpp" -int main() { printf("Hello World!\n"); } +const int WINDOW_WIDTH = 800; +const int WINDOW_HEIGHT = 600; + +void renderParticles(SDL_Renderer* renderer, const ParticleEmitter& emitter) { + for (const auto& particle : emitter.particles) { + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + SDL_Rect rect = { particle.position.x, particle.position.y, 5, 5 }; + SDL_RenderFillRect(renderer, &rect); + } +} + +int main(int argc, char* argv[]) { + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + std::cerr << "SDL Initialization Error: " << SDL_GetError() << std::endl; + return 1; + } + + SDL_Window* window = SDL_CreateWindow("Particle System", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + WINDOW_WIDTH, + WINDOW_HEIGHT, + SDL_WINDOW_SHOWN); + if (!window) { + std::cerr << "Window Creation Error: " << SDL_GetError() << std::endl; + SDL_Quit(); + return 1; + } + + // Create SDL renderer + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!renderer) { + std::cerr << "Renderer Creation Error: " << SDL_GetError() << std::endl; + SDL_DestroyWindow(window); + SDL_Quit(); + return 1; + } + + ParticleEmitter emitter(100, 10.0f); + emitter.position = { 400, 300 }; + ParticleEmitter emitter2(100, 10.0f); + emitter2.position = { 600, 600 }; + + float deltaTime = 0.1f; + + bool running = true; + SDL_Event event; + while (running) { + + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + running = false; + } + } + + emitter.update(deltaTime); + emitter2.update(deltaTime); + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + renderParticles(renderer, emitter); + renderParticles(renderer, emitter2); + + SDL_RenderPresent(renderer); + + + } + + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} -- cgit v1.2.3 From ef44da3f5e9ca533782da5e185e69e28c295d226 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Tue, 1 Oct 2024 19:53:56 +0200 Subject: Added resources to crepe --- asset/texture/img.png | Bin 0 -> 92742 bytes src/CMakeLists.txt | 21 +++++++++- src/build.sh | 7 ++++ src/crepe/CMakeLists.txt | 9 +++- src/crepe/api/Audio_asset.cpp | 16 ++++++++ src/crepe/api/Audio_asset.h | 18 ++++++++ src/crepe/api/CMakeLists.txt | 17 ++++++++ src/crepe/api/Image_asset.cpp | 14 +++++++ src/crepe/api/Image_asset.h | 18 ++++++++ src/crepe/api/map_asset.cpp | 12 ++++++ src/crepe/api/map_asset.h | 14 +++++++ src/crepe/api/resource.h | 23 +++++++++++ src/crepe/api/resource_manager.cpp | 25 ++++++++++++ src/crepe/api/resource_manager.h | 59 +++++++++++++++++++++++++++ src/crepe/api/spritesheet.cpp | 16 ++++++++ src/crepe/api/spritesheet.h | 21 ++++++++++ src/crepe/fabricator/CMakeLists.txt | 8 ++++ src/crepe/fabricator/resource_fabricator.cpp | 26 ++++++++++++ src/crepe/fabricator/resource_fabricator.h | 29 +++++++++++++ src/crepe/main.cpp | 3 -- src/dummy_resource_manager.cpp | 23 +++++++++++ 21 files changed, 373 insertions(+), 6 deletions(-) create mode 100644 asset/texture/img.png create mode 100755 src/build.sh create mode 100644 src/crepe/api/Audio_asset.cpp create mode 100644 src/crepe/api/Audio_asset.h create mode 100644 src/crepe/api/CMakeLists.txt create mode 100644 src/crepe/api/Image_asset.cpp create mode 100644 src/crepe/api/Image_asset.h create mode 100644 src/crepe/api/map_asset.cpp create mode 100644 src/crepe/api/map_asset.h create mode 100644 src/crepe/api/resource.h create mode 100644 src/crepe/api/resource_manager.cpp create mode 100644 src/crepe/api/resource_manager.h create mode 100644 src/crepe/api/spritesheet.cpp create mode 100644 src/crepe/api/spritesheet.h create mode 100644 src/crepe/fabricator/CMakeLists.txt create mode 100644 src/crepe/fabricator/resource_fabricator.cpp create mode 100644 src/crepe/fabricator/resource_fabricator.h delete mode 100644 src/crepe/main.cpp create mode 100644 src/dummy_resource_manager.cpp diff --git a/asset/texture/img.png b/asset/texture/img.png new file mode 100644 index 0000000..43b1eca Binary files /dev/null and b/asset/texture/img.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0090188..87330d4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,9 +8,28 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS 1) set(CMAKE_BUILD_TYPE Debug) add_compile_definitions(DEBUG) +#add_subdirectory(../lib/soloud soloud) + project(crepe C CXX) -add_executable(main) +add_library(crepe SHARED) + +target_include_directories(crepe + PUBLIC SYSTEM INTERFACE . +) + +# TODO: libraries should be linked as PRIVATE +target_link_libraries(crepe + #PUBLIC soloud +) add_subdirectory(crepe) +install( + TARGETS crepe + FILE_SET HEADERS DESTINATION include/crepe +) + + +add_executable(dummy_rm dummy_resource_manager.cpp) +target_link_libraries(dummy_rm PUBLIC crepe) diff --git a/src/build.sh b/src/build.sh new file mode 100755 index 0000000..e987bc1 --- /dev/null +++ b/src/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# creates the build dir and runs CMake with Ninja +cmake -B build -G Ninja + +# build the project +cmake --build build \ No newline at end of file diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 392b7d7..68aa072 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -1,3 +1,8 @@ -target_sources(main PUBLIC - main.cpp +target_sources(crepe PUBLIC ) + +target_sources(crepe PUBLIC FILE_SET HEADERS FILES +) + +add_subdirectory(api) +add_subdirectory(fabricator) diff --git a/src/crepe/api/Audio_asset.cpp b/src/crepe/api/Audio_asset.cpp new file mode 100644 index 0000000..a9b04ed --- /dev/null +++ b/src/crepe/api/Audio_asset.cpp @@ -0,0 +1,16 @@ + + + + +#include "Audio_asset.h" +#include + + +using namespace crepe::api; + +Audio::Audio(const std::string& content){ + this->m_content = content; +} + +Audio::~Audio(){ +} diff --git a/src/crepe/api/Audio_asset.h b/src/crepe/api/Audio_asset.h new file mode 100644 index 0000000..0b8e48e --- /dev/null +++ b/src/crepe/api/Audio_asset.h @@ -0,0 +1,18 @@ +#pragma once + + +#include "resource.h" +#include + + +namespace crepe::api { + + +class Audio : public Resource { + +public: + Audio(const std::string&); + ~Audio(); + +}; +} diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt new file mode 100644 index 0000000..7b16fb1 --- /dev/null +++ b/src/crepe/api/CMakeLists.txt @@ -0,0 +1,17 @@ +target_sources(crepe PUBLIC + Image_asset.cpp + map_asset.cpp + Audio_asset.cpp + spritesheet.cpp + resource_manager.cpp +) + +target_sources(crepe PUBLIC FILE_SET HEADERS FILES + resource.h + Image_asset.h + map_asset.h + Audio_asset.h + spritesheet.h + resource_manager.h +) + diff --git a/src/crepe/api/Image_asset.cpp b/src/crepe/api/Image_asset.cpp new file mode 100644 index 0000000..57431c4 --- /dev/null +++ b/src/crepe/api/Image_asset.cpp @@ -0,0 +1,14 @@ + + +#include "Image_asset.h" +#include + +using namespace crepe::api; + +Texture::Texture(const std::string& content){ + this->m_content = content; +} + + +Texture::~Texture(){ +} diff --git a/src/crepe/api/Image_asset.h b/src/crepe/api/Image_asset.h new file mode 100644 index 0000000..69549af --- /dev/null +++ b/src/crepe/api/Image_asset.h @@ -0,0 +1,18 @@ +#pragma once + + + +#include "resource.h" +#include + +namespace crepe::api { + + +class Texture : public Resource { + +public: + Texture(const std::string&); + ~Texture(); +}; + +} diff --git a/src/crepe/api/map_asset.cpp b/src/crepe/api/map_asset.cpp new file mode 100644 index 0000000..bbabe2b --- /dev/null +++ b/src/crepe/api/map_asset.cpp @@ -0,0 +1,12 @@ + + + + +#include "map_asset.h" + +Map::Map(const std::string& content){ + this->m_content = content; +} + +Map::~Map(){ +} diff --git a/src/crepe/api/map_asset.h b/src/crepe/api/map_asset.h new file mode 100644 index 0000000..a3b994f --- /dev/null +++ b/src/crepe/api/map_asset.h @@ -0,0 +1,14 @@ +#pragma once + +#include "resource.h" +#include + + +using namespace crepe::api; + +class Map : public Resource { + +public: + Map(const std::string& ); + ~Map(); +}; diff --git a/src/crepe/api/resource.h b/src/crepe/api/resource.h new file mode 100644 index 0000000..e6456f9 --- /dev/null +++ b/src/crepe/api/resource.h @@ -0,0 +1,23 @@ +#pragma once + + +#include + +namespace crepe::api { + +class Resource{ + +public: + + virtual ~Resource() =default; + + const std::string& getContent() const{ + return this->m_content; + } + +protected: + std::string m_content; +}; + + +} diff --git a/src/crepe/api/resource_manager.cpp b/src/crepe/api/resource_manager.cpp new file mode 100644 index 0000000..a5644ee --- /dev/null +++ b/src/crepe/api/resource_manager.cpp @@ -0,0 +1,25 @@ + + +#include "resource_manager.h" +#include +#include + +using namespace crepe::api; + +ResourceManager* ResourceManager::get_instance(){ + static ResourceManager instance; + return &instance; +} + + +ResourceManager::~ResourceManager(){ + m_resources.clear(); +} + + +void ResourceManager::Unload(const std::string& file_path){ + if(m_resources.find(file_path) != m_resources.end()){ + m_resources.erase(file_path); + } +} + diff --git a/src/crepe/api/resource_manager.h b/src/crepe/api/resource_manager.h new file mode 100644 index 0000000..1b91524 --- /dev/null +++ b/src/crepe/api/resource_manager.h @@ -0,0 +1,59 @@ +#pragma once + + + +#include +#include +#include +#include + + +#include "api/resource.h" +#include "fabricator/resource_fabricator.h" + + + namespace crepe::api{ + +class ResourceManager{ + + +private: + + std::unordered_map< std::string, std::unique_ptr> m_resources; + + +protected: + ResourceManager() = default; + ~ResourceManager(); + +public: + ResourceManager(const ResourceManager &) = delete; + ResourceManager(ResourceManager &&) = delete; + ResourceManager &operator=(const ResourceManager &) = delete; + ResourceManager &operator=(ResourceManager &&) = delete; + + static ResourceManager& get_instance(); + + + +public: + template + T* Load(const std::string& file_path){ + + if (m_resources.find(file_path) != m_resources.end()) { + return static_cast(m_resources[file_path].get()); + } + + std::unique_ptr resource = ResourceFactory::create_resource(file_path); + if (resource) { + m_resources[file_path] = std::move(resource); + return static_cast(m_resources[file_path].get() ); + } + + return nullptr; + } + + void Unload(const std::string& file_path); + +}; +} diff --git a/src/crepe/api/spritesheet.cpp b/src/crepe/api/spritesheet.cpp new file mode 100644 index 0000000..f42a782 --- /dev/null +++ b/src/crepe/api/spritesheet.cpp @@ -0,0 +1,16 @@ + + +#include "spritesheet.h" + +#include + +using namespace crepe::api; + +SpriteSheet::SpriteSheet(const std::string& content){ + this->m_content = content; +} + +SpriteSheet::~SpriteSheet(){ +} + + diff --git a/src/crepe/api/spritesheet.h b/src/crepe/api/spritesheet.h new file mode 100644 index 0000000..7f49156 --- /dev/null +++ b/src/crepe/api/spritesheet.h @@ -0,0 +1,21 @@ +#pragma once + + + + +#include "resource.h" +#include + + +namespace crepe::api { + + + +class SpriteSheet : public Resource{ + +public: + SpriteSheet(const std::string&); + ~SpriteSheet(); + +}; +} diff --git a/src/crepe/fabricator/CMakeLists.txt b/src/crepe/fabricator/CMakeLists.txt new file mode 100644 index 0000000..4fd7eea --- /dev/null +++ b/src/crepe/fabricator/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(crepe PUBLIC + resource_fabricator.cpp +) + +target_sources(crepe PUBLIC FILE_SET HEADERS FILES + resource_fabricator.h +) + diff --git a/src/crepe/fabricator/resource_fabricator.cpp b/src/crepe/fabricator/resource_fabricator.cpp new file mode 100644 index 0000000..0633a40 --- /dev/null +++ b/src/crepe/fabricator/resource_fabricator.cpp @@ -0,0 +1,26 @@ + + +#include "resource_fabricator.h" +#include +#include +#include +#include + + + + +std::string ResourceFactory::convert_file_to_string(const std::string& path){ + std::ifstream file(path, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + std::cerr << "Failed to open file: " << path << std::endl; + return ""; + } + + std::ifstream::pos_type fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + + std::vector bytes(fileSize); + file.read(bytes.data(), fileSize); + + return std::string(bytes.begin(), bytes.end()); +} diff --git a/src/crepe/fabricator/resource_fabricator.h b/src/crepe/fabricator/resource_fabricator.h new file mode 100644 index 0000000..9299ed3 --- /dev/null +++ b/src/crepe/fabricator/resource_fabricator.h @@ -0,0 +1,29 @@ +#pragma once + + + +#include "api/resource.h" +#include +#include + + + + +class ResourceFactory { + +public: + + template + static std::unique_ptr create_resource(const std::string& file_path){ + + return std::make_unique(convert_file_to_string(file_path)); + } + +private: + static std::string convert_file_to_string(const std::string& path); + +}; + + + + diff --git a/src/crepe/main.cpp b/src/crepe/main.cpp deleted file mode 100644 index 8e9a184..0000000 --- a/src/crepe/main.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -int main() { printf("Hello World!\n"); } diff --git a/src/dummy_resource_manager.cpp b/src/dummy_resource_manager.cpp new file mode 100644 index 0000000..214c617 --- /dev/null +++ b/src/dummy_resource_manager.cpp @@ -0,0 +1,23 @@ + + + + +#include "api/Image_asset.h" +#include "api/resource_manager.h" +#include +#include + + + +using namespace crepe; + +int main(){ + + // get instance of resource manager + api::ResourceManager& c_ResMan = api::ResourceManager::get_instance(); + + // make a resouce from the file path + api::Texture* img = c_ResMan.Load("../asset/texture/img.png"); + + std::cout << img->getContent() << std::endl; +} -- cgit v1.2.3 From e0ea870fdfcfbe9e3f0e47215bb809d4353d88e2 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Tue, 1 Oct 2024 21:22:30 +0200 Subject: removed submodule and updating resource_manager --- lib/SDL_image/CMakeLists.txt | 97 ++++++++++++++++++++++++++++++ src/CMakeLists.txt | 5 +- src/crepe/CMakeLists.txt | 5 +- src/crepe/Sound.cpp | 60 ------------------ src/crepe/Sound.h | 82 ------------------------- src/crepe/SoundContext.cpp | 20 ------ src/crepe/SoundContext.h | 26 -------- src/crepe/api/AudioSource.cpp | 2 +- src/crepe/api/Audio_asset.cpp | 16 ----- src/crepe/api/Audio_asset.h | 18 ------ src/crepe/api/CMakeLists.txt | 21 +++---- src/crepe/api/Image_asset.h | 2 +- src/crepe/api/map_asset.h | 2 +- src/crepe/api/resource.h | 23 ------- src/crepe/api/resource_manager.cpp | 4 +- src/crepe/api/resource_manager.h | 2 +- src/crepe/api/spritesheet.h | 2 +- src/crepe/fabricator/resource_fabricator.h | 2 +- src/crepe/facade/CMakeLists.txt | 13 ++++ src/crepe/facade/SdlContext.cpp | 57 ++++++++++++++++++ src/crepe/facade/SdlContext.h | 33 ++++++++++ src/crepe/facade/Sound.cpp | 60 ++++++++++++++++++ src/crepe/facade/Sound.h | 82 +++++++++++++++++++++++++ src/crepe/facade/SoundContext.cpp | 20 ++++++ src/crepe/facade/SoundContext.h | 26 ++++++++ src/crepe/facade/Texture.cpp | 31 ++++++++++ src/crepe/facade/Texture.h | 25 ++++++++ src/crepe/facade/touch | 0 src/dummy_audio.cpp | 2 +- 29 files changed, 468 insertions(+), 270 deletions(-) create mode 100644 lib/SDL_image/CMakeLists.txt delete mode 100644 src/crepe/Sound.cpp delete mode 100644 src/crepe/Sound.h delete mode 100644 src/crepe/SoundContext.cpp delete mode 100644 src/crepe/SoundContext.h delete mode 100644 src/crepe/api/Audio_asset.cpp delete mode 100644 src/crepe/api/Audio_asset.h delete mode 100644 src/crepe/api/resource.h create mode 100644 src/crepe/facade/CMakeLists.txt create mode 100644 src/crepe/facade/SdlContext.cpp create mode 100644 src/crepe/facade/SdlContext.h create mode 100644 src/crepe/facade/Sound.cpp create mode 100644 src/crepe/facade/Sound.h create mode 100644 src/crepe/facade/SoundContext.cpp create mode 100644 src/crepe/facade/SoundContext.h create mode 100644 src/crepe/facade/Texture.cpp create mode 100644 src/crepe/facade/Texture.h create mode 100644 src/crepe/facade/touch diff --git a/lib/SDL_image/CMakeLists.txt b/lib/SDL_image/CMakeLists.txt new file mode 100644 index 0000000..aaccd96 --- /dev/null +++ b/lib/SDL_image/CMakeLists.txt @@ -0,0 +1,97 @@ +cmake_minimum_required(VERSION 3.28) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) + +add_compile_definitions(WITH_SDL2) + +add_subdirectory(../sdl2 sdl2) + +project(soloud C CXX) + +add_library(soloud SHARED + src/src/audiosource/ay/chipplayer.cpp + src/src/audiosource/ay/sndbuffer.cpp + src/src/audiosource/ay/sndchip.cpp + src/src/audiosource/ay/sndrender.cpp + src/src/audiosource/ay/soloud_ay.cpp + src/src/audiosource/monotone/soloud_monotone.cpp + src/src/audiosource/noise/soloud_noise.cpp + src/src/audiosource/openmpt/soloud_openmpt.cpp + src/src/audiosource/openmpt/soloud_openmpt_dll.c + src/src/audiosource/sfxr/soloud_sfxr.cpp + src/src/audiosource/speech/darray.cpp + src/src/audiosource/speech/klatt.cpp + src/src/audiosource/speech/resonator.cpp + src/src/audiosource/speech/soloud_speech.cpp + src/src/audiosource/speech/tts.cpp + src/src/audiosource/tedsid/sid.cpp + src/src/audiosource/tedsid/soloud_tedsid.cpp + src/src/audiosource/tedsid/ted.cpp + src/src/audiosource/vic/soloud_vic.cpp + src/src/audiosource/vizsn/soloud_vizsn.cpp + src/src/audiosource/wav/dr_impl.cpp + src/src/audiosource/wav/soloud_wav.cpp + src/src/audiosource/wav/soloud_wavstream.cpp + src/src/audiosource/wav/stb_vorbis.c + + src/src/backend/alsa/soloud_alsa.cpp + src/src/backend/coreaudio/soloud_coreaudio.cpp + src/src/backend/jack/soloud_jack.cpp + src/src/backend/miniaudio/soloud_miniaudio.cpp + src/src/backend/nosound/soloud_nosound.cpp + src/src/backend/null/soloud_null.cpp + src/src/backend/openal/soloud_openal.cpp + src/src/backend/openal/soloud_openal_dll.c + src/src/backend/opensles/soloud_opensles.cpp + src/src/backend/oss/soloud_oss.cpp + src/src/backend/portaudio/soloud_portaudio.cpp + src/src/backend/portaudio/soloud_portaudio_dll.c + src/src/backend/sdl/soloud_sdl1.cpp + src/src/backend/sdl/soloud_sdl1_dll.c + src/src/backend/sdl/soloud_sdl2.cpp + src/src/backend/sdl/soloud_sdl2_dll.c + src/src/backend/sdl2_static/soloud_sdl2_static.cpp + src/src/backend/sdl_static/soloud_sdl_static.cpp + src/src/backend/wasapi/soloud_wasapi.cpp + src/src/backend/winmm/soloud_winmm.cpp + src/src/backend/xaudio2/soloud_xaudio2.cpp + + src/src/core/soloud.cpp + src/src/core/soloud_audiosource.cpp + src/src/core/soloud_bus.cpp + src/src/core/soloud_core_3d.cpp + src/src/core/soloud_core_basicops.cpp + src/src/core/soloud_core_faderops.cpp + src/src/core/soloud_core_filterops.cpp + src/src/core/soloud_core_getters.cpp + src/src/core/soloud_core_setters.cpp + src/src/core/soloud_core_voicegroup.cpp + src/src/core/soloud_core_voiceops.cpp + src/src/core/soloud_fader.cpp + src/src/core/soloud_fft.cpp + src/src/core/soloud_fft_lut.cpp + src/src/core/soloud_file.cpp + src/src/core/soloud_filter.cpp + src/src/core/soloud_misc.cpp + src/src/core/soloud_queue.cpp + src/src/core/soloud_thread.cpp + + # src/src/filter/soloud_bassboostfilter.cpp + # src/src/filter/soloud_biquadresonantfilter.cpp + # src/src/filter/soloud_dcremovalfilter.cpp + # src/src/filter/soloud_duckfilter.cpp + # src/src/filter/soloud_echofilter.cpp + # src/src/filter/soloud_eqfilter.cpp + # src/src/filter/soloud_fftfilter.cpp + # src/src/filter/soloud_flangerfilter.cpp + # src/src/filter/soloud_freeverbfilter.cpp + # src/src/filter/soloud_lofifilter.cpp + # src/src/filter/soloud_robotizefilter.cpp + # src/src/filter/soloud_waveshaperfilter.cpp +) +target_include_directories(soloud PRIVATE src/include) +target_include_directories(soloud SYSTEM INTERFACE src/include) + +target_link_libraries(soloud PRIVATE SDL2) + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cb310af..27e5188 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_BUILD_TYPE Debug) add_compile_definitions(DEBUG) add_subdirectory(../lib/soloud soloud) +add_subdirectory(../lib/SDL_image SDL_image) project(crepe C CXX) @@ -21,6 +22,8 @@ target_include_directories(crepe # TODO: libraries should be linked as PRIVATE target_link_libraries(crepe PUBLIC soloud + PUBLIC SDL2 + PUBLIC SDL2_image ) add_subdirectory(crepe) @@ -30,5 +33,5 @@ install( FILE_SET HEADERS DESTINATION include/crepe ) -add_executable(dummy_rm dummy_resource_manager.cpp) +add_executable(dummy_rm dummy_audio.cpp) target_link_libraries(dummy_rm PUBLIC crepe) diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index ed6ebe5..69e67ac 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -1,14 +1,11 @@ target_sources(crepe PUBLIC - Sound.cpp - SoundContext.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - Sound.h - SoundContext.h ) add_subdirectory(api) add_subdirectory(util) add_subdirectory(fabricator) +add_subdirectory(facade) diff --git a/src/crepe/Sound.cpp b/src/crepe/Sound.cpp deleted file mode 100644 index e1150ac..0000000 --- a/src/crepe/Sound.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "util/log.h" - -#include "Sound.h" -#include "SoundContext.h" - -using namespace crepe; - -Sound::Sound(std::unique_ptr res) { - dbg_trace(); - this->load(std::move(res)); -} - -Sound::Sound(const char * src) { - dbg_trace(); - this->load(std::make_unique(src)); -} - -void Sound::load(std::unique_ptr res) { - this->sample.load(res->canonical()); -} - -void Sound::play() { - SoundContext & ctx = SoundContext::get_instance(); - if (ctx.engine.getPause(this->handle)) { - // resume if paused - ctx.engine.setPause(this->handle, false); - } else { - // or start new sound - this->handle = ctx.engine.play(this->sample, this->volume); - ctx.engine.setLooping(this->handle, this->looping); - } -} - -void Sound::pause() { - SoundContext & ctx = SoundContext::get_instance(); - if (ctx.engine.getPause(this->handle)) return; - ctx.engine.setPause(this->handle, true); -} - -void Sound::rewind() { - SoundContext & ctx = SoundContext::get_instance(); - if (!ctx.engine.isValidVoiceHandle(this->handle)) return; - ctx.engine.seek(this->handle, 0); -} - -void Sound::set_volume(float volume) { - this->volume = volume; - - SoundContext & ctx = SoundContext::get_instance(); - if (!ctx.engine.isValidVoiceHandle(this->handle)) return; - ctx.engine.setVolume(this->handle, this->volume); -} - -void Sound::set_looping(bool looping) { - this->looping = looping; - - SoundContext & ctx = SoundContext::get_instance(); - if (!ctx.engine.isValidVoiceHandle(this->handle)) return; - ctx.engine.setLooping(this->handle, this->looping); -} diff --git a/src/crepe/Sound.h b/src/crepe/Sound.h deleted file mode 100644 index ac93991..0000000 --- a/src/crepe/Sound.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include "api/Resource.h" - -namespace crepe { - -class Sound { -public: - /** - * \brief Pause this sample - * - * Pauses this sound if it is playing, or does nothing if it is already - * paused. The playhead position is saved, such that calling \c play() after - * this function makes the sound resume. - */ - void pause(); - /** - * \brief Play this sample - * - * Resume playback if this sound is paused, or start from the beginning of - * the sample. - * - * \note This class only saves a reference to the most recent 'voice' of this - * sound. Calling \c play() while the sound is already playing causes - * multiple instances of the sample to play simultaniously. The sample - * started last is the one that is controlled afterwards. - */ - void play(); - /** - * \brief Reset playhead position - * - * Resets the playhead position so that calling \c play() after this function - * makes it play from the start of the sample. If the sound is not paused - * before calling this function, this function will stop playback. - */ - void rewind(); - /** - * \brief Set playback volume / gain - * - * \param volume Volume (0 = muted, 1 = full volume) - */ - void set_volume(float volume); - /** - * \brief Get playback volume / gain - * - * \return Volume - */ - float get_volume() const { return this->volume; } - /** - * \brief Set looping behavior for this sample - * - * \param looping Looping behavior (false = one-shot, true = loop) - */ - void set_looping(bool looping); - /** - * \brief Get looping behavior - * - * \return true if looping, false if one-shot - */ - bool get_looping() const { return this->looping; } - -public: - Sound(const char * src); - Sound(std::unique_ptr res); - -private: - void load(std::unique_ptr res); - -private: - SoLoud::Wav sample; - SoLoud::handle handle; - - float volume = 1.0f; - bool looping = false; -}; - -} // namespace crepe diff --git a/src/crepe/SoundContext.cpp b/src/crepe/SoundContext.cpp deleted file mode 100644 index 72047d2..0000000 --- a/src/crepe/SoundContext.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "util/log.h" - -#include "SoundContext.h" - -using namespace crepe; - -SoundContext & SoundContext::get_instance() { - static SoundContext instance; - return instance; -} - -SoundContext::SoundContext() { - dbg_trace(); - engine.init(); -} - -SoundContext::~SoundContext() { - dbg_trace(); - engine.deinit(); -} diff --git a/src/crepe/SoundContext.h b/src/crepe/SoundContext.h deleted file mode 100644 index 090966d..0000000 --- a/src/crepe/SoundContext.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -#include "Sound.h" - -namespace crepe { - -class SoundContext { -private: - SoundContext(); - virtual ~SoundContext(); - - // singleton - static SoundContext & get_instance(); - SoundContext(const SoundContext &) = delete; - SoundContext(SoundContext &&) = delete; - SoundContext & operator=(const SoundContext &) = delete; - SoundContext & operator=(SoundContext &&) = delete; - -private: - SoLoud::Soloud engine; - friend class Sound; -}; - -} // namespace crepe diff --git a/src/crepe/api/AudioSource.cpp b/src/crepe/api/AudioSource.cpp index 4d1b093..f0d708a 100644 --- a/src/crepe/api/AudioSource.cpp +++ b/src/crepe/api/AudioSource.cpp @@ -1,6 +1,6 @@ #include "AudioSource.h" -#include "../Sound.h" +#include "facade/Sound.h" #include using namespace crepe::api; diff --git a/src/crepe/api/Audio_asset.cpp b/src/crepe/api/Audio_asset.cpp deleted file mode 100644 index a9b04ed..0000000 --- a/src/crepe/api/Audio_asset.cpp +++ /dev/null @@ -1,16 +0,0 @@ - - - - -#include "Audio_asset.h" -#include - - -using namespace crepe::api; - -Audio::Audio(const std::string& content){ - this->m_content = content; -} - -Audio::~Audio(){ -} diff --git a/src/crepe/api/Audio_asset.h b/src/crepe/api/Audio_asset.h deleted file mode 100644 index 0b8e48e..0000000 --- a/src/crepe/api/Audio_asset.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - - -#include "resource.h" -#include - - -namespace crepe::api { - - -class Audio : public Resource { - -public: - Audio(const std::string&); - ~Audio(); - -}; -} diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 4f3fada..2cf0bcc 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -1,18 +1,17 @@ target_sources(crepe PUBLIC - Image_asset.cpp - map_asset.cpp - Audio_asset.cpp - spritesheet.cpp - resource_manager.cpp + #Image_asset.cpp + #map_asset.cpp + #spritesheet.cpp + #resource_manager.cpp + Resource.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - resource.h - Image_asset.h - map_asset.h - Audio_asset.h - spritesheet.h - resource_manager.h + Resource.h + #Image_asset.h + #map_asset.h + #spritesheet.h + #resource_manager.h Component.h AudioSource.h ) diff --git a/src/crepe/api/Image_asset.h b/src/crepe/api/Image_asset.h index 69549af..0a36b0b 100644 --- a/src/crepe/api/Image_asset.h +++ b/src/crepe/api/Image_asset.h @@ -2,7 +2,7 @@ -#include "resource.h" +#include "Resource.h" #include namespace crepe::api { diff --git a/src/crepe/api/map_asset.h b/src/crepe/api/map_asset.h index a3b994f..a4f3df7 100644 --- a/src/crepe/api/map_asset.h +++ b/src/crepe/api/map_asset.h @@ -1,6 +1,6 @@ #pragma once -#include "resource.h" +#include "Resource.h" #include diff --git a/src/crepe/api/resource.h b/src/crepe/api/resource.h deleted file mode 100644 index e6456f9..0000000 --- a/src/crepe/api/resource.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - - -#include - -namespace crepe::api { - -class Resource{ - -public: - - virtual ~Resource() =default; - - const std::string& getContent() const{ - return this->m_content; - } - -protected: - std::string m_content; -}; - - -} diff --git a/src/crepe/api/resource_manager.cpp b/src/crepe/api/resource_manager.cpp index a5644ee..0ecdae5 100644 --- a/src/crepe/api/resource_manager.cpp +++ b/src/crepe/api/resource_manager.cpp @@ -6,9 +6,9 @@ using namespace crepe::api; -ResourceManager* ResourceManager::get_instance(){ +ResourceManager& ResourceManager::get_instance(){ static ResourceManager instance; - return &instance; + return instance; } diff --git a/src/crepe/api/resource_manager.h b/src/crepe/api/resource_manager.h index 1b91524..5b0e0e1 100644 --- a/src/crepe/api/resource_manager.h +++ b/src/crepe/api/resource_manager.h @@ -8,7 +8,7 @@ #include -#include "api/resource.h" +#include "Resource.h" #include "fabricator/resource_fabricator.h" diff --git a/src/crepe/api/spritesheet.h b/src/crepe/api/spritesheet.h index 7f49156..e7530c2 100644 --- a/src/crepe/api/spritesheet.h +++ b/src/crepe/api/spritesheet.h @@ -3,7 +3,7 @@ -#include "resource.h" +#include "Resource.h" #include diff --git a/src/crepe/fabricator/resource_fabricator.h b/src/crepe/fabricator/resource_fabricator.h index 9299ed3..2b0030d 100644 --- a/src/crepe/fabricator/resource_fabricator.h +++ b/src/crepe/fabricator/resource_fabricator.h @@ -2,7 +2,7 @@ -#include "api/resource.h" +#include "api/Resource.h" #include #include diff --git a/src/crepe/facade/CMakeLists.txt b/src/crepe/facade/CMakeLists.txt new file mode 100644 index 0000000..1263683 --- /dev/null +++ b/src/crepe/facade/CMakeLists.txt @@ -0,0 +1,13 @@ +target_sources(crepe PUBLIC + Sound.cpp + SoundContext.cpp + Texture.cpp + SdlContext.cpp +) + +target_sources(crepe PUBLIC FILE_SET HEADERS FILES + Sound.h + SoundContext.h + Texture.h + SdlContext.h +) diff --git a/src/crepe/facade/SdlContext.cpp b/src/crepe/facade/SdlContext.cpp new file mode 100644 index 0000000..fc68b40 --- /dev/null +++ b/src/crepe/facade/SdlContext.cpp @@ -0,0 +1,57 @@ + + +#include "SdlContext.h" +#include +#include +#include +#include +#include + +using namespace crepe; + + +SdlContext& SdlContext::get_instance(){ + static SdlContext instance; + return instance; +} + + +SdlContext::SdlContext(){ + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; + return; + } + + m_game_window = SDL_CreateWindow("Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN); + if (!m_game_window) { + std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl; + } + + m_game_renderer = SDL_CreateRenderer(m_game_window, -1, SDL_RENDERER_ACCELERATED); + if (!m_game_renderer) { + std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() << std::endl; + SDL_DestroyWindow(m_game_window); + return; + } + + IMG_Init(IMG_INIT_PNG); +} + +SdlContext::~SdlContext(){ + if(m_game_renderer) + SDL_DestroyRenderer(m_game_renderer); + + if (m_game_window) { + SDL_DestroyWindow(m_game_window); + } + IMG_Quit(); +} + + +SDL_Texture* SdlContext::setTextureFromPath(const char* path){ + SDL_Surface* tmp = IMG_Load(path); + SDL_Texture* CreatedTexture = SDL_CreateTextureFromSurface(m_game_renderer, tmp); + SDL_FreeSurface(tmp); + + return CreatedTexture; +} diff --git a/src/crepe/facade/SdlContext.h b/src/crepe/facade/SdlContext.h new file mode 100644 index 0000000..c275300 --- /dev/null +++ b/src/crepe/facade/SdlContext.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Texture.h" +#include +#include +#include + +namespace crepe { + +class SdlContext { + +private: + SdlContext(); + virtual ~SdlContext(); + + // singleton + static SdlContext & get_instance(); + SdlContext(const SdlContext &) = delete; + SdlContext(SdlContext &&) = delete; + SdlContext & operator=(const SdlContext &) = delete; + SdlContext & operator=(SdlContext &&) = delete; + + SDL_Texture* setTextureFromPath(const char*); + +private: + friend class Texture; + + SDL_Window* m_game_window; + SDL_Renderer* m_game_renderer; +}; + +} // + diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp new file mode 100644 index 0000000..e1150ac --- /dev/null +++ b/src/crepe/facade/Sound.cpp @@ -0,0 +1,60 @@ +#include "util/log.h" + +#include "Sound.h" +#include "SoundContext.h" + +using namespace crepe; + +Sound::Sound(std::unique_ptr res) { + dbg_trace(); + this->load(std::move(res)); +} + +Sound::Sound(const char * src) { + dbg_trace(); + this->load(std::make_unique(src)); +} + +void Sound::load(std::unique_ptr res) { + this->sample.load(res->canonical()); +} + +void Sound::play() { + SoundContext & ctx = SoundContext::get_instance(); + if (ctx.engine.getPause(this->handle)) { + // resume if paused + ctx.engine.setPause(this->handle, false); + } else { + // or start new sound + this->handle = ctx.engine.play(this->sample, this->volume); + ctx.engine.setLooping(this->handle, this->looping); + } +} + +void Sound::pause() { + SoundContext & ctx = SoundContext::get_instance(); + if (ctx.engine.getPause(this->handle)) return; + ctx.engine.setPause(this->handle, true); +} + +void Sound::rewind() { + SoundContext & ctx = SoundContext::get_instance(); + if (!ctx.engine.isValidVoiceHandle(this->handle)) return; + ctx.engine.seek(this->handle, 0); +} + +void Sound::set_volume(float volume) { + this->volume = volume; + + SoundContext & ctx = SoundContext::get_instance(); + if (!ctx.engine.isValidVoiceHandle(this->handle)) return; + ctx.engine.setVolume(this->handle, this->volume); +} + +void Sound::set_looping(bool looping) { + this->looping = looping; + + SoundContext & ctx = SoundContext::get_instance(); + if (!ctx.engine.isValidVoiceHandle(this->handle)) return; + ctx.engine.setLooping(this->handle, this->looping); +} diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h new file mode 100644 index 0000000..ac93991 --- /dev/null +++ b/src/crepe/facade/Sound.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include + +#include + +#include "api/Resource.h" + +namespace crepe { + +class Sound { +public: + /** + * \brief Pause this sample + * + * Pauses this sound if it is playing, or does nothing if it is already + * paused. The playhead position is saved, such that calling \c play() after + * this function makes the sound resume. + */ + void pause(); + /** + * \brief Play this sample + * + * Resume playback if this sound is paused, or start from the beginning of + * the sample. + * + * \note This class only saves a reference to the most recent 'voice' of this + * sound. Calling \c play() while the sound is already playing causes + * multiple instances of the sample to play simultaniously. The sample + * started last is the one that is controlled afterwards. + */ + void play(); + /** + * \brief Reset playhead position + * + * Resets the playhead position so that calling \c play() after this function + * makes it play from the start of the sample. If the sound is not paused + * before calling this function, this function will stop playback. + */ + void rewind(); + /** + * \brief Set playback volume / gain + * + * \param volume Volume (0 = muted, 1 = full volume) + */ + void set_volume(float volume); + /** + * \brief Get playback volume / gain + * + * \return Volume + */ + float get_volume() const { return this->volume; } + /** + * \brief Set looping behavior for this sample + * + * \param looping Looping behavior (false = one-shot, true = loop) + */ + void set_looping(bool looping); + /** + * \brief Get looping behavior + * + * \return true if looping, false if one-shot + */ + bool get_looping() const { return this->looping; } + +public: + Sound(const char * src); + Sound(std::unique_ptr res); + +private: + void load(std::unique_ptr res); + +private: + SoLoud::Wav sample; + SoLoud::handle handle; + + float volume = 1.0f; + bool looping = false; +}; + +} // namespace crepe diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp new file mode 100644 index 0000000..72047d2 --- /dev/null +++ b/src/crepe/facade/SoundContext.cpp @@ -0,0 +1,20 @@ +#include "util/log.h" + +#include "SoundContext.h" + +using namespace crepe; + +SoundContext & SoundContext::get_instance() { + static SoundContext instance; + return instance; +} + +SoundContext::SoundContext() { + dbg_trace(); + engine.init(); +} + +SoundContext::~SoundContext() { + dbg_trace(); + engine.deinit(); +} diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h new file mode 100644 index 0000000..090966d --- /dev/null +++ b/src/crepe/facade/SoundContext.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "Sound.h" + +namespace crepe { + +class SoundContext { +private: + SoundContext(); + virtual ~SoundContext(); + + // singleton + static SoundContext & get_instance(); + SoundContext(const SoundContext &) = delete; + SoundContext(SoundContext &&) = delete; + SoundContext & operator=(const SoundContext &) = delete; + SoundContext & operator=(SoundContext &&) = delete; + +private: + SoLoud::Soloud engine; + friend class Sound; +}; + +} // namespace crepe diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp new file mode 100644 index 0000000..c24312a --- /dev/null +++ b/src/crepe/facade/Texture.cpp @@ -0,0 +1,31 @@ + + +#include "util/log.h" + +#include "Texture.h" +#include "SdlContext.h" +#include + +using namespace crepe; + +Texture::Texture(std::unique_ptr res) { + dbg_trace(); + this->load(std::move(res)); +} + +Texture::Texture(const char * src) { + dbg_trace(); + this->load(std::make_unique(src)); +} + +Texture::~Texture(){ + dbg_trace(); + if(this->m_texture){ + SDL_DestroyTexture(m_texture); + } +} +void Texture::load(std::unique_ptr res) { + SdlContext& ctx = SdlContext::get_instance(); + m_texture = ctx.setTextureFromPath(res->canonical()); +} + diff --git a/src/crepe/facade/Texture.h b/src/crepe/facade/Texture.h new file mode 100644 index 0000000..3677f6e --- /dev/null +++ b/src/crepe/facade/Texture.h @@ -0,0 +1,25 @@ +#pragma once + +#include "api/Resource.h" +#include +#include + + +namespace crepe { + +class Texture { + +public: + Texture(const char * src); + Texture(std::unique_ptr res); + ~Texture(); + +private: + void load(std::unique_ptr res); + +private: + SDL_Texture* m_texture; +}; + +} // namespace crepe + diff --git a/src/crepe/facade/touch b/src/crepe/facade/touch new file mode 100644 index 0000000..e69de29 diff --git a/src/dummy_audio.cpp b/src/dummy_audio.cpp index 049bb49..211a503 100644 --- a/src/dummy_audio.cpp +++ b/src/dummy_audio.cpp @@ -1,4 +1,4 @@ -#include "crepe/Sound.h" +#include "crepe/facade/Sound.h" #include "crepe/util/log.h" #include -- cgit v1.2.3 From 2add1f75012dd14efb4d69ce11566ef4477e74c2 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Tue, 1 Oct 2024 21:48:26 +0200 Subject: need to switch submodule image to branch sdl2 --- .gitmodules | 3 ++ lib/SDL_image/CMakeLists.txt | 97 -------------------------------------------- lib/sdl_image/CMakeLists.txt | 21 ++++++++++ lib/sdl_image/src | 1 + src/CMakeLists.txt | 4 +- 5 files changed, 27 insertions(+), 99 deletions(-) delete mode 100644 lib/SDL_image/CMakeLists.txt create mode 100644 lib/sdl_image/CMakeLists.txt create mode 160000 lib/sdl_image/src diff --git a/.gitmodules b/.gitmodules index bb860c0..a20e230 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ path = lib/sdl2 url = https://github.com/libsdl-org/SDL shallow = true +[submodule "lib/sdl_image/src"] + path = lib/sdl_image/src + url = https://github.com/libsdl-org/SDL_image diff --git a/lib/SDL_image/CMakeLists.txt b/lib/SDL_image/CMakeLists.txt deleted file mode 100644 index aaccd96..0000000 --- a/lib/SDL_image/CMakeLists.txt +++ /dev/null @@ -1,97 +0,0 @@ -cmake_minimum_required(VERSION 3.28) - -set(CMAKE_C_STANDARD 11) -set(CMAKE_CXX_STANDARD 20) - -add_compile_definitions(WITH_SDL2) - -add_subdirectory(../sdl2 sdl2) - -project(soloud C CXX) - -add_library(soloud SHARED - src/src/audiosource/ay/chipplayer.cpp - src/src/audiosource/ay/sndbuffer.cpp - src/src/audiosource/ay/sndchip.cpp - src/src/audiosource/ay/sndrender.cpp - src/src/audiosource/ay/soloud_ay.cpp - src/src/audiosource/monotone/soloud_monotone.cpp - src/src/audiosource/noise/soloud_noise.cpp - src/src/audiosource/openmpt/soloud_openmpt.cpp - src/src/audiosource/openmpt/soloud_openmpt_dll.c - src/src/audiosource/sfxr/soloud_sfxr.cpp - src/src/audiosource/speech/darray.cpp - src/src/audiosource/speech/klatt.cpp - src/src/audiosource/speech/resonator.cpp - src/src/audiosource/speech/soloud_speech.cpp - src/src/audiosource/speech/tts.cpp - src/src/audiosource/tedsid/sid.cpp - src/src/audiosource/tedsid/soloud_tedsid.cpp - src/src/audiosource/tedsid/ted.cpp - src/src/audiosource/vic/soloud_vic.cpp - src/src/audiosource/vizsn/soloud_vizsn.cpp - src/src/audiosource/wav/dr_impl.cpp - src/src/audiosource/wav/soloud_wav.cpp - src/src/audiosource/wav/soloud_wavstream.cpp - src/src/audiosource/wav/stb_vorbis.c - - src/src/backend/alsa/soloud_alsa.cpp - src/src/backend/coreaudio/soloud_coreaudio.cpp - src/src/backend/jack/soloud_jack.cpp - src/src/backend/miniaudio/soloud_miniaudio.cpp - src/src/backend/nosound/soloud_nosound.cpp - src/src/backend/null/soloud_null.cpp - src/src/backend/openal/soloud_openal.cpp - src/src/backend/openal/soloud_openal_dll.c - src/src/backend/opensles/soloud_opensles.cpp - src/src/backend/oss/soloud_oss.cpp - src/src/backend/portaudio/soloud_portaudio.cpp - src/src/backend/portaudio/soloud_portaudio_dll.c - src/src/backend/sdl/soloud_sdl1.cpp - src/src/backend/sdl/soloud_sdl1_dll.c - src/src/backend/sdl/soloud_sdl2.cpp - src/src/backend/sdl/soloud_sdl2_dll.c - src/src/backend/sdl2_static/soloud_sdl2_static.cpp - src/src/backend/sdl_static/soloud_sdl_static.cpp - src/src/backend/wasapi/soloud_wasapi.cpp - src/src/backend/winmm/soloud_winmm.cpp - src/src/backend/xaudio2/soloud_xaudio2.cpp - - src/src/core/soloud.cpp - src/src/core/soloud_audiosource.cpp - src/src/core/soloud_bus.cpp - src/src/core/soloud_core_3d.cpp - src/src/core/soloud_core_basicops.cpp - src/src/core/soloud_core_faderops.cpp - src/src/core/soloud_core_filterops.cpp - src/src/core/soloud_core_getters.cpp - src/src/core/soloud_core_setters.cpp - src/src/core/soloud_core_voicegroup.cpp - src/src/core/soloud_core_voiceops.cpp - src/src/core/soloud_fader.cpp - src/src/core/soloud_fft.cpp - src/src/core/soloud_fft_lut.cpp - src/src/core/soloud_file.cpp - src/src/core/soloud_filter.cpp - src/src/core/soloud_misc.cpp - src/src/core/soloud_queue.cpp - src/src/core/soloud_thread.cpp - - # src/src/filter/soloud_bassboostfilter.cpp - # src/src/filter/soloud_biquadresonantfilter.cpp - # src/src/filter/soloud_dcremovalfilter.cpp - # src/src/filter/soloud_duckfilter.cpp - # src/src/filter/soloud_echofilter.cpp - # src/src/filter/soloud_eqfilter.cpp - # src/src/filter/soloud_fftfilter.cpp - # src/src/filter/soloud_flangerfilter.cpp - # src/src/filter/soloud_freeverbfilter.cpp - # src/src/filter/soloud_lofifilter.cpp - # src/src/filter/soloud_robotizefilter.cpp - # src/src/filter/soloud_waveshaperfilter.cpp -) -target_include_directories(soloud PRIVATE src/include) -target_include_directories(soloud SYSTEM INTERFACE src/include) - -target_link_libraries(soloud PRIVATE SDL2) - diff --git a/lib/sdl_image/CMakeLists.txt b/lib/sdl_image/CMakeLists.txt new file mode 100644 index 0000000..65f7598 --- /dev/null +++ b/lib/sdl_image/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.28) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) + +#add_compile_definitions(WITH_SDL2) +#add_subdirectory(../sdl2 sdl2) + +include_directories(src/include/SDL3_image) + +project(sdl_image C CXX) + +add_library(sdl_image SHARED + src/src/IMG.c + src/src/IMG_png.c +) +target_include_directories(sdl_image PRIVATE src/include/) +target_include_directories(sdl_image SYSTEM INTERFACE src/include/) + +target_link_libraries(sdl_image PRIVATE SDL2) + diff --git a/lib/sdl_image/src b/lib/sdl_image/src new file mode 160000 index 0000000..6f45843 --- /dev/null +++ b/lib/sdl_image/src @@ -0,0 +1 @@ +Subproject commit 6f4584340b9b78542d11bf0232a1a0862de1f0a9 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 27e5188..40d6644 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_BUILD_TYPE Debug) add_compile_definitions(DEBUG) add_subdirectory(../lib/soloud soloud) -add_subdirectory(../lib/SDL_image SDL_image) +add_subdirectory(../lib/sdl_image sdl_image) project(crepe C CXX) @@ -23,7 +23,7 @@ target_include_directories(crepe target_link_libraries(crepe PUBLIC soloud PUBLIC SDL2 - PUBLIC SDL2_image + PUBLIC sdl_image ) add_subdirectory(crepe) -- cgit v1.2.3 From 41bd601ef62d967c80cc0591eaf6b4290baae425 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Wed, 2 Oct 2024 10:18:51 +0200 Subject: removed submodule --- .gitmodules | 3 --- lib/sdl_image/src | 1 - 2 files changed, 4 deletions(-) delete mode 160000 lib/sdl_image/src diff --git a/.gitmodules b/.gitmodules index a20e230..bb860c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,3 @@ path = lib/sdl2 url = https://github.com/libsdl-org/SDL shallow = true -[submodule "lib/sdl_image/src"] - path = lib/sdl_image/src - url = https://github.com/libsdl-org/SDL_image diff --git a/lib/sdl_image/src b/lib/sdl_image/src deleted file mode 160000 index 6f45843..0000000 --- a/lib/sdl_image/src +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6f4584340b9b78542d11bf0232a1a0862de1f0a9 -- cgit v1.2.3 From de2c2593f9f272c5151d74af4ff846fdd70a9bc7 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Wed, 2 Oct 2024 15:57:59 +0200 Subject: working resource manager and textures and sprite to new standard --- .gitmodules | 4 ++ asset/spritesheet/spritesheet_test.png | Bin 0 -> 616 bytes lib/sdl_image/CMakeLists.txt | 34 ++++++++++++++- lib/sdl_image/src | 1 + src/CMakeLists.txt | 6 +-- src/crepe/api/CMakeLists.txt | 13 +++--- src/crepe/api/Image_asset.cpp | 14 ------- src/crepe/api/Image_asset.h | 18 -------- src/crepe/api/Resource.cpp | 6 ++- src/crepe/api/baseResource.h | 11 +++++ src/crepe/api/game.cpp | 18 ++++++++ src/crepe/api/game.h | 15 +++++++ src/crepe/api/resource_manager.h | 12 +++--- src/crepe/api/spritesheet.cpp | 36 ++++++++++++++-- src/crepe/api/spritesheet.h | 27 +++++++++--- src/crepe/facade/SdlContext.cpp | 73 ++++++++++++++++++++++++++++++++- src/crepe/facade/SdlContext.h | 15 +++++-- src/crepe/facade/Sound.h | 3 +- src/crepe/facade/Texture.cpp | 4 ++ src/crepe/facade/Texture.h | 8 +++- src/dummy_resource_manager.cpp | 31 ++++++++++---- 21 files changed, 273 insertions(+), 76 deletions(-) create mode 100644 asset/spritesheet/spritesheet_test.png create mode 160000 lib/sdl_image/src delete mode 100644 src/crepe/api/Image_asset.cpp delete mode 100644 src/crepe/api/Image_asset.h create mode 100644 src/crepe/api/baseResource.h create mode 100644 src/crepe/api/game.cpp create mode 100644 src/crepe/api/game.h diff --git a/.gitmodules b/.gitmodules index bb860c0..d7a6ebf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,7 @@ path = lib/sdl2 url = https://github.com/libsdl-org/SDL shallow = true +[submodule "lib/sdl_image/src"] + path = lib/sdl_image/src + url = https://github.com/libsdl-org/SDL_image + branch = SDL2 diff --git a/asset/spritesheet/spritesheet_test.png b/asset/spritesheet/spritesheet_test.png new file mode 100644 index 0000000..d68a72a Binary files /dev/null and b/asset/spritesheet/spritesheet_test.png differ diff --git a/lib/sdl_image/CMakeLists.txt b/lib/sdl_image/CMakeLists.txt index 65f7598..d1bee81 100644 --- a/lib/sdl_image/CMakeLists.txt +++ b/lib/sdl_image/CMakeLists.txt @@ -11,8 +11,38 @@ include_directories(src/include/SDL3_image) project(sdl_image C CXX) add_library(sdl_image SHARED - src/src/IMG.c - src/src/IMG_png.c + src/src/IMG.c + src/src/IMG_WIC.c + src/src/IMG_avif.c + src/src/IMG_bmp.c + src/src/IMG_gif.c + src/src/IMG_jpg.c + src/src/IMG_jxl.c + src/src/IMG_lbm.c + src/src/IMG_pcx.c + src/src/IMG_png.c + src/src/IMG_pnm.c + src/src/IMG_qoi.c + src/src/IMG_stb.c + src/src/IMG_svg.c + src/src/IMG_tga.c + src/src/IMG_tif.c + src/src/IMG_webp.c + src/src/IMG_xcf.c + src/src/IMG_xpm.c + src/src/IMG_xv.c + + + + + + + + + + + + ) target_include_directories(sdl_image PRIVATE src/include/) target_include_directories(sdl_image SYSTEM INTERFACE src/include/) diff --git a/lib/sdl_image/src b/lib/sdl_image/src new file mode 160000 index 0000000..c6c7278 --- /dev/null +++ b/lib/sdl_image/src @@ -0,0 +1 @@ +Subproject commit c6c7278b86b5de1232b10de8f612ed05cf2d11f6 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 40d6644..b47be8f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_BUILD_TYPE Debug) add_compile_definitions(DEBUG) add_subdirectory(../lib/soloud soloud) -add_subdirectory(../lib/sdl_image sdl_image) +add_subdirectory(../lib/sdl_image/src SDL2_image) project(crepe C CXX) @@ -23,7 +23,7 @@ target_include_directories(crepe target_link_libraries(crepe PUBLIC soloud PUBLIC SDL2 - PUBLIC sdl_image + PUBLIC SDL2_image ) add_subdirectory(crepe) @@ -33,5 +33,5 @@ install( FILE_SET HEADERS DESTINATION include/crepe ) -add_executable(dummy_rm dummy_audio.cpp) +add_executable(dummy_rm dummy_resource_manager.cpp) target_link_libraries(dummy_rm PUBLIC crepe) diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 2cf0bcc..96b55cf 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -1,17 +1,18 @@ target_sources(crepe PUBLIC - #Image_asset.cpp #map_asset.cpp - #spritesheet.cpp - #resource_manager.cpp + spritesheet.cpp + resource_manager.cpp Resource.cpp + game.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES Resource.h - #Image_asset.h + baseResource.h + game.h #map_asset.h - #spritesheet.h - #resource_manager.h + spritesheet.h + resource_manager.h Component.h AudioSource.h ) diff --git a/src/crepe/api/Image_asset.cpp b/src/crepe/api/Image_asset.cpp deleted file mode 100644 index 57431c4..0000000 --- a/src/crepe/api/Image_asset.cpp +++ /dev/null @@ -1,14 +0,0 @@ - - -#include "Image_asset.h" -#include - -using namespace crepe::api; - -Texture::Texture(const std::string& content){ - this->m_content = content; -} - - -Texture::~Texture(){ -} diff --git a/src/crepe/api/Image_asset.h b/src/crepe/api/Image_asset.h deleted file mode 100644 index 0a36b0b..0000000 --- a/src/crepe/api/Image_asset.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - - - -#include "Resource.h" -#include - -namespace crepe::api { - - -class Texture : public Resource { - -public: - Texture(const std::string&); - ~Texture(); -}; - -} diff --git a/src/crepe/api/Resource.cpp b/src/crepe/api/Resource.cpp index 1a647ce..f7f2516 100644 --- a/src/crepe/api/Resource.cpp +++ b/src/crepe/api/Resource.cpp @@ -1,11 +1,15 @@ #include +#include +#include #include "Resource.h" +#include "util/log.h" using namespace crepe::api; Resource::Resource(const std::string & src) { - this->src = std::filesystem::canonical(src); + dbg_trace(); + this->src = std::filesystem::path(src); this->file = std::ifstream(this->src, std::ios::in | std::ios::binary); } diff --git a/src/crepe/api/baseResource.h b/src/crepe/api/baseResource.h new file mode 100644 index 0000000..2513f4d --- /dev/null +++ b/src/crepe/api/baseResource.h @@ -0,0 +1,11 @@ +#pragma once + +namespace crepe::api { + +class BaseResource { +public: + virtual ~BaseResource() = default; +}; + +} + diff --git a/src/crepe/api/game.cpp b/src/crepe/api/game.cpp new file mode 100644 index 0000000..02a0132 --- /dev/null +++ b/src/crepe/api/game.cpp @@ -0,0 +1,18 @@ + + + +#include "game.h" +#include "api/spritesheet.h" +#include "facade/SdlContext.h" +#include "facade/Texture.h" +#include + + +void game::render(std::vector & draw, std::vector & ss){ + auto& ctx = crepe::SdlContext::get_instance(); + + ctx.loop(*draw[0], *ss[0]); +} + + + diff --git a/src/crepe/api/game.h b/src/crepe/api/game.h new file mode 100644 index 0000000..7cde954 --- /dev/null +++ b/src/crepe/api/game.h @@ -0,0 +1,15 @@ +#pragma once + + +#include "api/spritesheet.h" +#include "facade/Texture.h" +#include + +class game { + +public: + game(){} + ~game(){} + + void render(std::vector&, std::vector&); +}; diff --git a/src/crepe/api/resource_manager.h b/src/crepe/api/resource_manager.h index 5b0e0e1..a646d95 100644 --- a/src/crepe/api/resource_manager.h +++ b/src/crepe/api/resource_manager.h @@ -7,19 +7,17 @@ #include #include +#include "api/baseResource.h" -#include "Resource.h" -#include "fabricator/resource_fabricator.h" - - namespace crepe::api{ +namespace crepe::api{ class ResourceManager{ private: - std::unordered_map< std::string, std::unique_ptr> m_resources; + std::unordered_map< std::string, std::unique_ptr> m_resources; protected: @@ -44,10 +42,10 @@ public: return static_cast(m_resources[file_path].get()); } - std::unique_ptr resource = ResourceFactory::create_resource(file_path); + auto resource = std::make_unique(file_path.c_str()); if (resource) { m_resources[file_path] = std::move(resource); - return static_cast(m_resources[file_path].get() ); + return static_cast(m_resources[file_path].get() ); } return nullptr; diff --git a/src/crepe/api/spritesheet.cpp b/src/crepe/api/spritesheet.cpp index f42a782..93a2b65 100644 --- a/src/crepe/api/spritesheet.cpp +++ b/src/crepe/api/spritesheet.cpp @@ -1,16 +1,44 @@ #include "spritesheet.h" +#include "SDL_rect.h" +#include "SDL_render.h" +#include "api/Resource.h" +#include "facade/SdlContext.h" +#include -#include using namespace crepe::api; -SpriteSheet::SpriteSheet(const std::string& content){ - this->m_content = content; +Spritesheet::Spritesheet(const char* src, const int row, const int col){ + this->load(std::make_unique(src), row, col); } -SpriteSheet::~SpriteSheet(){ +Spritesheet::Spritesheet(std::unique_ptr res, const int row, const int col){ + this->load(std::move(res), row, col); } +Spritesheet::~Spritesheet(){ + + if (this->m_spritesheet) { + SDL_DestroyTexture(this->m_spritesheet); + } +} + +void Spritesheet::select_sprite(const int x, const int y){ + m_clip.x = x * m_clip.w; + m_clip.y = y * m_clip.h; +} + +void Spritesheet::draw_selected_sprite(const int x, const int y){ + auto& ctx = SdlContext::get_instance(); + SDL_Rect tmp = { x, y, m_clip.w, m_clip.h}; + SDL_RenderCopy(ctx.m_game_renderer, this->m_spritesheet, &this->m_clip, &tmp); +} + +void Spritesheet::load(std::unique_ptr res, const int row, const int col){ + auto& ctx = SdlContext::get_instance(); + + this->m_spritesheet = ctx.setTextureFromPath(res->canonical(), this->m_clip, row, col); +} diff --git a/src/crepe/api/spritesheet.h b/src/crepe/api/spritesheet.h index e7530c2..7f46296 100644 --- a/src/crepe/api/spritesheet.h +++ b/src/crepe/api/spritesheet.h @@ -4,18 +4,35 @@ #include "Resource.h" -#include +#include "SDL_rect.h" +#include "SDL_render.h" +#include namespace crepe::api { +class Spritesheet{ +public: + Spritesheet(const char * src, const int row , const int col); + Spritesheet(std::unique_ptr res, const int row, const int col); + ~Spritesheet(); -class SpriteSheet : public Resource{ + void select_sprite(const int x, const int y); + void draw_selected_sprite(const int x, const int y); +private: + void load(std::unique_ptr res, const int row, const int col);; + SDL_Texture* get_texture() const; -public: - SpriteSheet(const std::string&); - ~SpriteSheet(); + +private: + + SDL_Texture* m_spritesheet; + SDL_Rect m_clip; + + friend class SdlContext; }; + } + diff --git a/src/crepe/facade/SdlContext.cpp b/src/crepe/facade/SdlContext.cpp index fc68b40..7e2d79f 100644 --- a/src/crepe/facade/SdlContext.cpp +++ b/src/crepe/facade/SdlContext.cpp @@ -1,11 +1,18 @@ #include "SdlContext.h" +#include "SDL_rect.h" +#include "api/spritesheet.h" +#include "facade/Texture.h" +#include "util/log.h" #include #include #include #include #include +#include +#include +#include using namespace crepe; @@ -33,8 +40,11 @@ SdlContext::SdlContext(){ SDL_DestroyWindow(m_game_window); return; } - - IMG_Init(IMG_INIT_PNG); + int imgFlags = IMG_INIT_PNG; + if( !( IMG_Init( imgFlags ) & imgFlags ) ) + { + std::cout << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError() << std::endl; + } } SdlContext::~SdlContext(){ @@ -45,13 +55,72 @@ SdlContext::~SdlContext(){ SDL_DestroyWindow(m_game_window); } IMG_Quit(); + SDL_Quit(); } +SDL_Texture* SdlContext::setTextureFromPath(const char* path, SDL_Rect& clip, const int row, const int col){ + dbg_trace(); + + SDL_Surface* tmp = IMG_Load(path); + if (!tmp) { + std::cerr << "Error surface " << IMG_GetError << std::endl; + } + + clip.w = tmp->w / col; + clip.h = tmp->h / row; + + SDL_Texture* CreatedTexture = SDL_CreateTextureFromSurface(m_game_renderer, tmp); + + if (!CreatedTexture) { + std::cerr << "Error could not create texture " << IMG_GetError << std::endl; + } + SDL_FreeSurface(tmp); + + return CreatedTexture; +} SDL_Texture* SdlContext::setTextureFromPath(const char* path){ + dbg_trace(); + SDL_Surface* tmp = IMG_Load(path); + if (!tmp) { + std::cerr << "Error surface " << IMG_GetError << std::endl; + } SDL_Texture* CreatedTexture = SDL_CreateTextureFromSurface(m_game_renderer, tmp); + + if (!CreatedTexture) { + std::cerr << "Error could not create texture " << IMG_GetError << std::endl; + } SDL_FreeSurface(tmp); return CreatedTexture; } + + +void SdlContext::loop(const Texture& texture, api::Spritesheet& ss){ + SDL_RenderClear(m_game_renderer); + bool quit = false; + SDL_Event event; + + while (!quit) { + Uint32 ticks = SDL_GetTicks(); + int sprite = (ticks / 100) % 4; + ss.select_sprite(sprite, 0); + + while (SDL_PollEvent(&event) != NULL) { + switch (event.type) { + case SDL_QUIT: + quit = true; + break; + } + + } + + SDL_RenderClear(m_game_renderer); + SDL_RenderCopy(m_game_renderer, texture.get_texture(), NULL, NULL); + ss.draw_selected_sprite(10, 10); + SDL_RenderPresent(m_game_renderer); + } +} + + diff --git a/src/crepe/facade/SdlContext.h b/src/crepe/facade/SdlContext.h index c275300..329a374 100644 --- a/src/crepe/facade/SdlContext.h +++ b/src/crepe/facade/SdlContext.h @@ -1,29 +1,38 @@ #pragma once +#include "SDL_rect.h" #include "Texture.h" +#include "api/spritesheet.h" #include #include -#include namespace crepe { +class Texture; +class Spritesheet; + class SdlContext { +public: + void loop(const Texture& , api::Spritesheet&); + + // singleton + static SdlContext & get_instance(); private: SdlContext(); virtual ~SdlContext(); - // singleton - static SdlContext & get_instance(); SdlContext(const SdlContext &) = delete; SdlContext(SdlContext &&) = delete; SdlContext & operator=(const SdlContext &) = delete; SdlContext & operator=(SdlContext &&) = delete; SDL_Texture* setTextureFromPath(const char*); + SDL_Texture* setTextureFromPath(const char*, SDL_Rect& clip, const int row, const int col); private: friend class Texture; + friend class api::Spritesheet; SDL_Window* m_game_window; SDL_Renderer* m_game_renderer; diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index ac93991..06e1932 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -6,10 +6,11 @@ #include #include "api/Resource.h" +#include "api/baseResource.h" namespace crepe { -class Sound { +class Sound : public api::BaseResource{ public: /** * \brief Pause this sample diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp index c24312a..220ef2e 100644 --- a/src/crepe/facade/Texture.cpp +++ b/src/crepe/facade/Texture.cpp @@ -25,7 +25,11 @@ Texture::~Texture(){ } } void Texture::load(std::unique_ptr res) { + dbg_trace(); SdlContext& ctx = SdlContext::get_instance(); m_texture = ctx.setTextureFromPath(res->canonical()); } +SDL_Texture* Texture::get_texture() const{ + return m_texture; +} diff --git a/src/crepe/facade/Texture.h b/src/crepe/facade/Texture.h index 3677f6e..a5fcca9 100644 --- a/src/crepe/facade/Texture.h +++ b/src/crepe/facade/Texture.h @@ -1,24 +1,28 @@ #pragma once +#include "api/baseResource.h" +#include "facade/SdlContext.h" #include "api/Resource.h" #include #include - namespace crepe { -class Texture { +class Texture : public api::BaseResource{ public: Texture(const char * src); Texture(std::unique_ptr res); ~Texture(); + SDL_Texture* get_texture() const; private: void load(std::unique_ptr res); private: SDL_Texture* m_texture; + + friend class SdlContext; }; } // namespace crepe diff --git a/src/dummy_resource_manager.cpp b/src/dummy_resource_manager.cpp index 214c617..bb0b7af 100644 --- a/src/dummy_resource_manager.cpp +++ b/src/dummy_resource_manager.cpp @@ -2,22 +2,37 @@ -#include "api/Image_asset.h" +#include "api/game.h" #include "api/resource_manager.h" -#include -#include - - +#include "api/spritesheet.h" +#include "facade/Texture.h" +#include "util/log.h" +#include using namespace crepe; int main(){ + + dbg_trace(); + // get instance of resource manager + //api::ResourceManager& c_ResMan = api::ResourceManager::get_instance(); + + + game engine; + api::ResourceManager& c_ResMan = api::ResourceManager::get_instance(); - // make a resouce from the file path - api::Texture* img = c_ResMan.Load("../asset/texture/img.png"); + auto test = c_ResMan.Load("../asset/texture/img.png"); + + auto img = Texture("../asset/texture/img.png"); + + auto SS = api::Spritesheet("../asset/spritesheet/spritesheet_test.png", 1 , 4); + + std::vector t = {test}; + std::vector s = {&SS}; + + engine.render(t, s); - std::cout << img->getContent() << std::endl; } -- cgit v1.2.3 From 94d591b69c45470480db477bc4000ef35019e18b Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Wed, 2 Oct 2024 16:10:22 +0200 Subject: created event classes and start of eventManager --- mwe/events/include/event.h | 93 ++++++++++++++++++++++----------------- mwe/events/include/eventHandler.h | 4 +- mwe/events/include/eventManager.h | 18 ++++++++ 3 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 mwe/events/include/eventManager.h diff --git a/mwe/events/include/event.h b/mwe/events/include/event.h index 8774a7d..f5f426a 100644 --- a/mwe/events/include/event.h +++ b/mwe/events/include/event.h @@ -3,6 +3,7 @@ #include #include #include +#include using Keycode = uint16_t; enum : Keycode { // From glfw3.h @@ -146,62 +147,72 @@ public: return ++id; } }; -class Event { -public: - virtual ~Event() = default; - virtual std::uint32_t GetEventType() const = 0; - - virtual std::string ToString() const { - return std::to_string(GetEventType()); - }; - - bool isHandled { false }; -}; #define EVENT_TYPE(event_type) \ static std::uint32_t GetStaticEventType() \ { \ static std::uint32_t type = UUIDGenerator::GetUniqueID(); \ return type; \ } \ - std::uint32_t GetEventType() const override \ + std::uint32_t GetEventId() const override \ { \ return GetStaticEventType(); \ } +class Event { + public: + Event(std::string eventType) { + eventData["eventType"] = eventType; + } + virtual ~Event() = default; + + virtual std::uint32_t GetEventId() const = 0; + virtual std::string toString() const { + return std::to_string(GetEventId()); + } + + void addArgument(const std::string& key, const std::variant& value); + + std::variant getArgument(const std::string& key) const; + + std::string getType() const; + bool isHandled() const; + void markHandled(); + private: + std::unordered_map> eventData; + bool isHandled = false; +}; class KeyPressedEvent : public Event { + public: + EVENT_TYPE("KeyPressedEvent"); + KeyPressedEvent(int keyCode); + Keycode getKeyCode() const; + int getRepeatCount() const; + private: + Keycode keycode; public: - EVENT_TYPE("KeyPressedEvent"); - - KeyPressedEvent(int keyCode, int repeatCount) - : key(keyCode) - , repeatCount(repeatCount) - { - } - std::string ToString() const override - { - return "KeyPressedEvent KeyPressed" + std::to_string(key); - } - -public: - Keycode key { 0 }; - int repeatCount { 0 }; + Keycode key = 0; + int repeatCount = 0; }; class KeyReleasedEvent : public Event { + public: + EVENT_TYPE("KeyReleasedEvent"); + KeyReleasedEvent(int keyCode); + Keycode getKeyCode(); + private: + Keycode keycode; public: - EVENT_TYPE("KeyReleased"); - - KeyReleasedEvent(int keyCode) - : key(keyCode) - { - } - std::string ToString() const override - { - return "KeyPressedEvent KeyPressed" + std::to_string(key); - } - -public: - Keycode key { 0 }; + Keycode key = 0; }; - +class MousePressedEvent : public Event { + public: + EVENT_TYPE("MousePressedEvent"); + MousePressedEvent(int mouseX,int mouseY); + std::pair getMousePosition(); + private: + Keycode keycode; + int mouseX = 0; + int mouseY = 0; + +}; diff --git a/mwe/events/include/eventHandler.h b/mwe/events/include/eventHandler.h index c17c586..03017dd 100644 --- a/mwe/events/include/eventHandler.h +++ b/mwe/events/include/eventHandler.h @@ -4,7 +4,7 @@ #include template using EventHandler = std::function; -class EventHandlerWrapperInterface { +class IEventHandlerWrapper{ public: void Exec(const Event& e) { @@ -18,7 +18,7 @@ private: }; template -class EventHandlerWrapper : public EventHandlerWrapperInterface { +class EventHandlerWrapper : public IEventHandlerWrapper { public: explicit EventHandlerWrapper(const EventHandler& handler) : m_handler(handler) diff --git a/mwe/events/include/eventManager.h b/mwe/events/include/eventManager.h new file mode 100644 index 0000000..93def36 --- /dev/null +++ b/mwe/events/include/eventManager.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include +#include "event.h" +#include "eventHandler.h" +using EventType = int; +using EventId = int; +class EventManager{ + public: + + void subscribe(EventId eventId,std::unique_ptr&& handler); + void unsubscribe(EventId eventId, const std::string& handlerName); + void dispatchEvents(); + void triggerEvent(EventId eventId); + void queueEvent(); + private: + std::unordered_map>> m_subscribers; +}; -- cgit v1.2.3 From 035444c1b7ee2e76c235e16eafd6115e849eec73 Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Thu, 3 Oct 2024 09:40:40 +0200 Subject: changed sound back --- src/CMakeLists.txt | 1 + src/crepe/facade/Sound.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b47be8f..3d29a54 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,4 +34,5 @@ install( ) add_executable(dummy_rm dummy_resource_manager.cpp) +#add_executable(dummy_rm dummy_audio.cpp) target_link_libraries(dummy_rm PUBLIC crepe) diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index 06e1932..2f1c924 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -10,7 +10,7 @@ namespace crepe { -class Sound : public api::BaseResource{ +class Sound{ public: /** * \brief Pause this sample -- cgit v1.2.3 From c39adf3040c3f2f7cfcab5ce5b7e39b815e8131d Mon Sep 17 00:00:00 2001 From: Max-001 <80035972+Max-001@users.noreply.github.com> Date: Sat, 5 Oct 2024 11:05:59 +0200 Subject: Setup ecs-memory-efficient --- .vscode/launch.json | 2 +- .vscode/tasks.json | 6 +- mwe/ecs-homemade/CMakeLists.txt | 17 ---- mwe/ecs-homemade/inc/ComponentManager.h | 48 ---------- mwe/ecs-homemade/inc/ComponentManager.hpp | 88 ------------------ mwe/ecs-homemade/inc/Components.h | 29 ------ mwe/ecs-homemade/inc/GameObjectMax.h | 20 ---- mwe/ecs-homemade/inc/GameObjectMax.hpp | 6 -- mwe/ecs-homemade/src/ComponentManager.cpp | 21 ----- mwe/ecs-homemade/src/Components.cpp | 12 --- mwe/ecs-homemade/src/GameObjectMax.cpp | 5 - mwe/ecs-homemade/src/main.cpp | 108 ---------------------- mwe/ecs-memory-efficient/CMakeLists.txt | 17 ++++ mwe/ecs-memory-efficient/inc/ComponentManager.h | 48 ++++++++++ mwe/ecs-memory-efficient/inc/ComponentManager.hpp | 88 ++++++++++++++++++ mwe/ecs-memory-efficient/inc/Components.h | 33 +++++++ mwe/ecs-memory-efficient/inc/GameObjectMax.h | 20 ++++ mwe/ecs-memory-efficient/inc/GameObjectMax.hpp | 6 ++ mwe/ecs-memory-efficient/src/ComponentManager.cpp | 21 +++++ mwe/ecs-memory-efficient/src/Components.cpp | 10 ++ mwe/ecs-memory-efficient/src/GameObjectMax.cpp | 5 + mwe/ecs-memory-efficient/src/main.cpp | 108 ++++++++++++++++++++++ 22 files changed, 360 insertions(+), 358 deletions(-) delete mode 100644 mwe/ecs-homemade/CMakeLists.txt delete mode 100644 mwe/ecs-homemade/inc/ComponentManager.h delete mode 100644 mwe/ecs-homemade/inc/ComponentManager.hpp delete mode 100644 mwe/ecs-homemade/inc/Components.h delete mode 100644 mwe/ecs-homemade/inc/GameObjectMax.h delete mode 100644 mwe/ecs-homemade/inc/GameObjectMax.hpp delete mode 100644 mwe/ecs-homemade/src/ComponentManager.cpp delete mode 100644 mwe/ecs-homemade/src/Components.cpp delete mode 100644 mwe/ecs-homemade/src/GameObjectMax.cpp delete mode 100644 mwe/ecs-homemade/src/main.cpp create mode 100644 mwe/ecs-memory-efficient/CMakeLists.txt create mode 100644 mwe/ecs-memory-efficient/inc/ComponentManager.h create mode 100644 mwe/ecs-memory-efficient/inc/ComponentManager.hpp create mode 100644 mwe/ecs-memory-efficient/inc/Components.h create mode 100644 mwe/ecs-memory-efficient/inc/GameObjectMax.h create mode 100644 mwe/ecs-memory-efficient/inc/GameObjectMax.hpp create mode 100644 mwe/ecs-memory-efficient/src/ComponentManager.cpp create mode 100644 mwe/ecs-memory-efficient/src/Components.cpp create mode 100644 mwe/ecs-memory-efficient/src/GameObjectMax.cpp create mode 100644 mwe/ecs-memory-efficient/src/main.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index 160d242..69e3590 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "name": "Debug with Ninja", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/mwe/ecs-homemade/build/ecs-homemade.exe", + "program": "${workspaceFolder}/mwe/ecs-memory-efficient/build/ecs-memory-efficient.exe", "args": [], "stopAtEntry": true, "cwd": "${workspaceFolder}", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 19afa6f..b00cb77 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,8 +9,8 @@ "-G", "Ninja", "-B", - "${workspaceFolder}/mwe/ecs-homemade/build", // Create build folder here - "${workspaceFolder}/mwe/ecs-homemade" // Path to your source directory + "${workspaceFolder}/mwe/ecs-memory-efficient/build", // Create build folder here + "${workspaceFolder}/mwe/ecs-memory-efficient" // Path to your source directory ], "group": { "kind": "build", @@ -24,7 +24,7 @@ "command": "cmake", "args": [ "--build", - "${workspaceFolder}/mwe/ecs-homemade/build" // Build directory + "${workspaceFolder}/mwe/ecs-memory-efficient/build" // Build directory ], "dependsOn": "configure", // Ensure the configure task runs first "group": { diff --git a/mwe/ecs-homemade/CMakeLists.txt b/mwe/ecs-homemade/CMakeLists.txt deleted file mode 100644 index 6267c1a..0000000 --- a/mwe/ecs-homemade/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(ecs-homemade) - -# Set the C++ standard (optional, but good practice) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -# Use the debug mode (otherwise breakpoints are not compiled) -set(CMAKE_BUILD_TYPE Debug) - -add_executable(ecs-homemade - src/main.cpp - src/ComponentManager.cpp - src/Components.cpp - src/GameObjectMax.cpp -) -target_include_directories(ecs-homemade PRIVATE "${CMAKE_SOURCE_DIR}/inc") diff --git a/mwe/ecs-homemade/inc/ComponentManager.h b/mwe/ecs-homemade/inc/ComponentManager.h deleted file mode 100644 index 893aa56..0000000 --- a/mwe/ecs-homemade/inc/ComponentManager.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "Components.h" -#include -#include -#include -#include -#include -#include - -class ComponentManager { -public: - static ComponentManager& GetInstance(); //Singleton - - ComponentManager(const ComponentManager&) = delete; //Singleton - ComponentManager(ComponentManager&&) = delete; //Singleton - ComponentManager& operator=(const ComponentManager&) = delete; //Singleton - ComponentManager& operator=(ComponentManager&&) = delete; //Singleton - - template - void AddComponent(std::uint32_t id, Args&&... args); //Add a component of a specific type - template - void DeleteComponentsById(std::uint32_t id); //Deletes all components of a specific type and id - template - void DeleteComponents(); //Deletes all components of a specific type - void DeleteAllComponentsOfId(std::uint32_t id); //Deletes all components of a specific id - void DeleteAllComponents(); //Deletes all components - - template - std::vector> GetComponentsByID(std::uint32_t id) const; //Get a vector<> of all components at specific type and id - template - std::vector, std::uint32_t>> GetComponentsByType() const; //Get a vector<> of all components of a specific type - -private: - static ComponentManager mInstance; //Singleton - - ComponentManager(); //Singleton - - /* - * The std::unordered_map>>> below might seem a bit strange, let me explain this structure: - * The std::unordered_map<> has a key and value. The key is a std::type_index and the value is a std::vector. So, a new std::vector will be created for each new std::type_index. - * The first std::vector<> stores another vector<>. This first vector<> is to bind the entity's id to a component. - * The second std::vector<> stores unique_ptrs. Each component can be gathered via an unique_ptr. This second vector<> allows multiple components of the same std::type_index for one entity (id). - */ - std::unordered_map>>> mComponents; -}; - -#include "ComponentManager.hpp" diff --git a/mwe/ecs-homemade/inc/ComponentManager.hpp b/mwe/ecs-homemade/inc/ComponentManager.hpp deleted file mode 100644 index 53dfddd..0000000 --- a/mwe/ecs-homemade/inc/ComponentManager.hpp +++ /dev/null @@ -1,88 +0,0 @@ - -template -void ComponentManager::AddComponent(std::uint32_t id, Args&&... args) { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - if (mComponents.find(type) == mComponents.end()) { //Check if this component type is already in the unordered_map<> - mComponents[type] = std::vector>>(); //If not, create a new (empty) vector<> of vector> - } - - if (id >= mComponents[type].size()) { //Resize the vector<> if the id is greater than the current size - mComponents[type].resize(id + 1); //Initialize new slots to nullptr (resize does automatically init to nullptr) - } - - mComponents[type][id].push_back(std::make_unique(std::forward(args)...)); //Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> -} - -template -void ComponentManager::DeleteComponentsById(std::uint32_t id) { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - std::vector>>& componentArray = mComponents[type]; //Get the correct vector<> - - if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - componentArray[id].clear(); //Clear the whole vector<> of this specific type and id - } - } -} - -template -void ComponentManager::DeleteComponents() { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - mComponents[type].clear(); //Clear the whole vector<> of this specific type - } -} - -template -std::vector> ComponentManager::GetComponentsByID(std::uint32_t id) const { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - std::vector> componentVector; //Create an empty vector<> - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - - const std::vector>>& componentArray = mComponents.at(type); //Get the correct vector<> - - if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - for (const std::unique_ptr& componentPtr : componentArray[id]) { //Loop trough the whole vector<> - T* castedComponent = static_cast(componentPtr.get()); //Cast the unique_ptr to a raw pointer - - if (castedComponent) { //Ensure that the cast was successful - componentVector.push_back(*castedComponent); //Add the dereferenced raw pointer to the vector<> - } - } - } - } - - return componentVector; //Return the vector<> -} - -template -std::vector, std::uint32_t>> ComponentManager::GetComponentsByType() const { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - std::vector, std::uint32_t>> componentVector; //Create an empty vector<> - std::uint32_t id = 0; //Set the id to 0 (the id will also be stored in the returned vector<>) - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - - const std::vector>>& componentArray = mComponents.at(type); //Get the correct vector<> - - for (const std::vector>& component : componentArray) { //Loop through the whole vector<> - for (const std::unique_ptr& componentPtr : component) { //Loop trough the whole vector<> - T* castedComponent = static_cast(componentPtr.get()); //Cast the unique_ptr to a raw pointer - - if (castedComponent) { //Ensure that the cast was successful - componentVector.emplace_back(std::ref(*castedComponent), id); //Pair the dereferenced raw pointer and the id and add it to the vector<> - } - } - - ++id; //Increase the id (the id will also be stored in the returned vector<>) - } - } - - return componentVector; //Return the vector<> -} diff --git a/mwe/ecs-homemade/inc/Components.h b/mwe/ecs-homemade/inc/Components.h deleted file mode 100644 index 2645a45..0000000 --- a/mwe/ecs-homemade/inc/Components.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -class Component { -public: - Component(); - - bool mActive; -}; - -class Sprite : public Component { -public: - void Render(); -}; - -class Rigidbody : public Component { -public: - Rigidbody(int mass, int gravityScale, int bodyType); - - int mMass; - int mGravityScale; - int mBodyType; -}; - -class Colider : public Component { -public: - Colider(int size); - - int mSize; -}; diff --git a/mwe/ecs-homemade/inc/GameObjectMax.h b/mwe/ecs-homemade/inc/GameObjectMax.h deleted file mode 100644 index f0bcec9..0000000 --- a/mwe/ecs-homemade/inc/GameObjectMax.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include - -class GameObject { -public: - GameObject(std::uint32_t id, std::string name, std::string tag, int layer); - - template - void AddComponent(Args&&... args); - - std::uint32_t mId; - std::string mName; - std::string mTag; - bool mActive; - int mLayer; -}; - -#include "GameObjectMax.hpp" diff --git a/mwe/ecs-homemade/inc/GameObjectMax.hpp b/mwe/ecs-homemade/inc/GameObjectMax.hpp deleted file mode 100644 index 1e952ba..0000000 --- a/mwe/ecs-homemade/inc/GameObjectMax.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "ComponentManager.h" - -template -void GameObject::AddComponent(Args&&... args) { - ComponentManager::GetInstance().AddComponent(mId, std::forward(args)...); -} diff --git a/mwe/ecs-homemade/src/ComponentManager.cpp b/mwe/ecs-homemade/src/ComponentManager.cpp deleted file mode 100644 index 16cc2b6..0000000 --- a/mwe/ecs-homemade/src/ComponentManager.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "ComponentManager.h" - -ComponentManager ComponentManager::mInstance; - -ComponentManager& ComponentManager::GetInstance() { - return mInstance; -} - -ComponentManager::ComponentManager() {} - -void ComponentManager::DeleteAllComponentsOfId(std::uint32_t id) { - for(auto& [type, componentArray] : mComponents) { //Loop through all the types (in the unordered_map<>) - if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - componentArray[id].clear(); //Clear the components at this specific id - } - } -} - -void ComponentManager::DeleteAllComponents() { - mComponents.clear(); //Clear the whole unordered_map<> -} diff --git a/mwe/ecs-homemade/src/Components.cpp b/mwe/ecs-homemade/src/Components.cpp deleted file mode 100644 index 5f10bc5..0000000 --- a/mwe/ecs-homemade/src/Components.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "Components.h" -#include - -Component::Component() : mActive(true) {} - -void Sprite::Render() { - std::cout << "Rendering sprite" << std::endl; -} - -Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} - -Colider::Colider(int size) : mSize(size) {} diff --git a/mwe/ecs-homemade/src/GameObjectMax.cpp b/mwe/ecs-homemade/src/GameObjectMax.cpp deleted file mode 100644 index 62c41de..0000000 --- a/mwe/ecs-homemade/src/GameObjectMax.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "GameObjectMax.h" - -#include "ComponentManager.h" - -GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, int layer) : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} diff --git a/mwe/ecs-homemade/src/main.cpp b/mwe/ecs-homemade/src/main.cpp deleted file mode 100644 index 9e230e5..0000000 --- a/mwe/ecs-homemade/src/main.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include -#include - -#include "ComponentManager.h" -#include "GameObjectMax.h" -#include "Components.h" - -int main() { - GameObject gameObect0(0, "Name: 0", "Tag: 0", 0); //Entity 0 - GameObject gameObect1(1, "Name: 1", "Tag: 1", 1); //Entity 1 - GameObject gameObect2(2, "Name: 2", "Tag: 2", 2); //Entity 2 - GameObject gameObect3(3, "Name: 3", "Tag: 3", 3); //Entity 3 - GameObject gameObect4(4, "Name: 4", "Tag: 4", 4); //Entity 4 - GameObject gameObect5(5, "Name: 5", "Tag: 5", 5); //Entity 5 - GameObject gameObect6(6, "Name: 6", "Tag: 6", 6); //Entity 6 - GameObject gameObect7(7, "Name: 7", "Tag: 7", 7); //Entity 7 - - gameObect0.AddComponent(); //Add a sprite to entity0 - gameObect0.AddComponent(1, 2, 3); //Also add a rigidbody to entity0 - gameObect0.AddComponent(3, 2, 1); //Add a second rigidbody to entity0 - - gameObect1.AddComponent(4, 5, 6); //Only add a rigidbody to entity1 - - gameObect2.AddComponent(); //Add four sprites to entity2 - gameObect2.AddComponent(); - gameObect2.AddComponent(); - gameObect2.AddComponent(); - gameObect2.AddComponent(10, 100, 500); //Add four rigidbodies to entity2 - gameObect2.AddComponent(10, 100, 501); - gameObect2.AddComponent(10, 100, 502); - gameObect2.AddComponent(10, 100, 500); - - //Add non components to entity3, entity4, entity5 and entity6 - - gameObect7.AddComponent(); //Add a sprite to entity 7 - gameObect7.AddComponent(30); //Add a colder to entity 7 - - //The entities are now initialized - //Now I will demonstrate some ways of retreiving/getting components - - std::cout << "Finding all sprites of entity 0" << std::endl; - std::vector> spriteOfEntity0 = ComponentManager::GetInstance().GetComponentsByID(gameObect0.mId); - for(Sprite& spriteEntity0 : spriteOfEntity0) { - std::cout << "Sprite of entity 0: " << spriteEntity0.mActive << std::endl; - } - std::cout << std::endl; - - std::cout << "Finding all rigidbodies of entity 0" << std::endl; - std::vector> rigidbodyOfEntity0 = ComponentManager::GetInstance().GetComponentsByID(gameObect0.mId); - for(Rigidbody& rigidbodyEntity0 : rigidbodyOfEntity0) { - std::cout << "Rigidbody of entity 0: " << rigidbodyEntity0.mMass << " " << rigidbodyEntity0.mGravityScale << " " << rigidbodyEntity0.mBodyType << std::endl; - - rigidbodyEntity0.mMass = 15; - } - std::cout << std::endl; - - std::cout << "Finding all coliders of entity 0" << std::endl; - std::vector> coliderOfEntity0 = ComponentManager::GetInstance().GetComponentsByID(gameObect0.mId); - for(Colider& coliderEntity0 : coliderOfEntity0) { - std::cout << "Colider of entity 0: " << coliderEntity0.mSize << std::endl; - } - std::cout << std::endl; - - std::cout << "Finding all sprites of entity 3" << std::endl; - std::vector> spriteOfEntity3 = ComponentManager::GetInstance().GetComponentsByID(gameObect3.mId); - for(Sprite& spriteEntity3 : spriteOfEntity3) { - std::cout << "Sprite of entity 3: " << spriteEntity3.mActive << std::endl; - } - std::cout << std::endl; - - std::cout << "Finding all rigidbodies of entity 3" << std::endl; - std::vector> rigidbodyOfEntity3 = ComponentManager::GetInstance().GetComponentsByID(gameObect3.mId); - for(Rigidbody& rigidbodyEntity3 : rigidbodyOfEntity3) { - std::cout << "Rigidbody of entity 3: " << rigidbodyEntity3.mMass << " " << rigidbodyEntity3.mGravityScale << " " << rigidbodyEntity3.mBodyType << std::endl; - } - std::cout << std::endl; - - std::cout << "Finding all sprites of all entities" << std::endl; - std::vector, std::uint32_t>> sprites = ComponentManager::GetInstance().GetComponentsByType(); - for(auto& [sprite, id] : sprites) { - std::cout << "Sprite of id: " << id << ": " << sprite.get().mActive << std::endl; - } - std::cout << std::endl; - - std::cout << "Finding all coliders of all entities" << std::endl; - std::vector, std::uint32_t>> coliders = ComponentManager::GetInstance().GetComponentsByType(); - for(auto& [colider, id] : coliders) { - std::cout << "Colder of id: " << id << ": " << colider.get().mSize << std::endl; - } - std::cout << std::endl; - - std::cout << "Finding all rigidbodies of all entities" << std::endl; - std::vector, std::uint32_t>> rigidBodies = ComponentManager::GetInstance().GetComponentsByType(); - for(auto& [rigidbody, id] : rigidBodies) { - std::cout << "Rigidbody of id: " << id << ": " << rigidbody.get().mMass << " " << rigidbody.get().mGravityScale << " " << rigidbody.get().mBodyType << std::endl; - - rigidbody.get().mMass = -1; - } - std::cout << std::endl; - - std::cout << "Finding all rigidbodies of all entities for the second time (after changing mMass to -1)" << std::endl; - std::vector, std::uint32_t>> rigidBodies2 = ComponentManager::GetInstance().GetComponentsByType(); - for(auto& [rigidbody2, id2] : rigidBodies2) { - std::cout << "Rigidbody of id: " << id2 << ": " << rigidbody2.get().mMass << " " << rigidbody2.get().mGravityScale << " " << rigidbody2.get().mBodyType << std::endl; - } - std::cout << std::endl; -} diff --git a/mwe/ecs-memory-efficient/CMakeLists.txt b/mwe/ecs-memory-efficient/CMakeLists.txt new file mode 100644 index 0000000..d072907 --- /dev/null +++ b/mwe/ecs-memory-efficient/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) +project(ecs-memory-efficient) + +# Set the C++ standard (optional, but good practice) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Use the debug mode (otherwise breakpoints are not compiled) +set(CMAKE_BUILD_TYPE Debug) + +add_executable(ecs-memory-efficient + src/main.cpp + src/ComponentManager.cpp + src/Components.cpp + src/GameObjectMax.cpp +) +target_include_directories(ecs-memory-efficient PRIVATE "${CMAKE_SOURCE_DIR}/inc") diff --git a/mwe/ecs-memory-efficient/inc/ComponentManager.h b/mwe/ecs-memory-efficient/inc/ComponentManager.h new file mode 100644 index 0000000..893aa56 --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ComponentManager.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Components.h" +#include +#include +#include +#include +#include +#include + +class ComponentManager { +public: + static ComponentManager& GetInstance(); //Singleton + + ComponentManager(const ComponentManager&) = delete; //Singleton + ComponentManager(ComponentManager&&) = delete; //Singleton + ComponentManager& operator=(const ComponentManager&) = delete; //Singleton + ComponentManager& operator=(ComponentManager&&) = delete; //Singleton + + template + void AddComponent(std::uint32_t id, Args&&... args); //Add a component of a specific type + template + void DeleteComponentsById(std::uint32_t id); //Deletes all components of a specific type and id + template + void DeleteComponents(); //Deletes all components of a specific type + void DeleteAllComponentsOfId(std::uint32_t id); //Deletes all components of a specific id + void DeleteAllComponents(); //Deletes all components + + template + std::vector> GetComponentsByID(std::uint32_t id) const; //Get a vector<> of all components at specific type and id + template + std::vector, std::uint32_t>> GetComponentsByType() const; //Get a vector<> of all components of a specific type + +private: + static ComponentManager mInstance; //Singleton + + ComponentManager(); //Singleton + + /* + * The std::unordered_map>>> below might seem a bit strange, let me explain this structure: + * The std::unordered_map<> has a key and value. The key is a std::type_index and the value is a std::vector. So, a new std::vector will be created for each new std::type_index. + * The first std::vector<> stores another vector<>. This first vector<> is to bind the entity's id to a component. + * The second std::vector<> stores unique_ptrs. Each component can be gathered via an unique_ptr. This second vector<> allows multiple components of the same std::type_index for one entity (id). + */ + std::unordered_map>>> mComponents; +}; + +#include "ComponentManager.hpp" diff --git a/mwe/ecs-memory-efficient/inc/ComponentManager.hpp b/mwe/ecs-memory-efficient/inc/ComponentManager.hpp new file mode 100644 index 0000000..53dfddd --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ComponentManager.hpp @@ -0,0 +1,88 @@ + +template +void ComponentManager::AddComponent(std::uint32_t id, Args&&... args) { + std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) + + if (mComponents.find(type) == mComponents.end()) { //Check if this component type is already in the unordered_map<> + mComponents[type] = std::vector>>(); //If not, create a new (empty) vector<> of vector> + } + + if (id >= mComponents[type].size()) { //Resize the vector<> if the id is greater than the current size + mComponents[type].resize(id + 1); //Initialize new slots to nullptr (resize does automatically init to nullptr) + } + + mComponents[type][id].push_back(std::make_unique(std::forward(args)...)); //Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> +} + +template +void ComponentManager::DeleteComponentsById(std::uint32_t id) { + std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) + + if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) + std::vector>>& componentArray = mComponents[type]; //Get the correct vector<> + + if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + componentArray[id].clear(); //Clear the whole vector<> of this specific type and id + } + } +} + +template +void ComponentManager::DeleteComponents() { + std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) + + if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) + mComponents[type].clear(); //Clear the whole vector<> of this specific type + } +} + +template +std::vector> ComponentManager::GetComponentsByID(std::uint32_t id) const { + std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) + + std::vector> componentVector; //Create an empty vector<> + + if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) + + const std::vector>>& componentArray = mComponents.at(type); //Get the correct vector<> + + if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + for (const std::unique_ptr& componentPtr : componentArray[id]) { //Loop trough the whole vector<> + T* castedComponent = static_cast(componentPtr.get()); //Cast the unique_ptr to a raw pointer + + if (castedComponent) { //Ensure that the cast was successful + componentVector.push_back(*castedComponent); //Add the dereferenced raw pointer to the vector<> + } + } + } + } + + return componentVector; //Return the vector<> +} + +template +std::vector, std::uint32_t>> ComponentManager::GetComponentsByType() const { + std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) + + std::vector, std::uint32_t>> componentVector; //Create an empty vector<> + std::uint32_t id = 0; //Set the id to 0 (the id will also be stored in the returned vector<>) + + if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) + + const std::vector>>& componentArray = mComponents.at(type); //Get the correct vector<> + + for (const std::vector>& component : componentArray) { //Loop through the whole vector<> + for (const std::unique_ptr& componentPtr : component) { //Loop trough the whole vector<> + T* castedComponent = static_cast(componentPtr.get()); //Cast the unique_ptr to a raw pointer + + if (castedComponent) { //Ensure that the cast was successful + componentVector.emplace_back(std::ref(*castedComponent), id); //Pair the dereferenced raw pointer and the id and add it to the vector<> + } + } + + ++id; //Increase the id (the id will also be stored in the returned vector<>) + } + } + + return componentVector; //Return the vector<> +} diff --git a/mwe/ecs-memory-efficient/inc/Components.h b/mwe/ecs-memory-efficient/inc/Components.h new file mode 100644 index 0000000..98c5fe7 --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/Components.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +class Component { +public: + Component(); + + bool mActive; +}; + +class Sprite : public Component { +public: + Sprite(std::string path); + + std::string mPath; +}; + +class Rigidbody : public Component { +public: + Rigidbody(int mass, int gravityScale, int bodyType); + + int mMass; + int mGravityScale; + int mBodyType; +}; + +class Colider : public Component { +public: + Colider(int size); + + int mSize; +}; diff --git a/mwe/ecs-memory-efficient/inc/GameObjectMax.h b/mwe/ecs-memory-efficient/inc/GameObjectMax.h new file mode 100644 index 0000000..f0bcec9 --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/GameObjectMax.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +class GameObject { +public: + GameObject(std::uint32_t id, std::string name, std::string tag, int layer); + + template + void AddComponent(Args&&... args); + + std::uint32_t mId; + std::string mName; + std::string mTag; + bool mActive; + int mLayer; +}; + +#include "GameObjectMax.hpp" diff --git a/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp b/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp new file mode 100644 index 0000000..1e952ba --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp @@ -0,0 +1,6 @@ +#include "ComponentManager.h" + +template +void GameObject::AddComponent(Args&&... args) { + ComponentManager::GetInstance().AddComponent(mId, std::forward(args)...); +} diff --git a/mwe/ecs-memory-efficient/src/ComponentManager.cpp b/mwe/ecs-memory-efficient/src/ComponentManager.cpp new file mode 100644 index 0000000..16cc2b6 --- /dev/null +++ b/mwe/ecs-memory-efficient/src/ComponentManager.cpp @@ -0,0 +1,21 @@ +#include "ComponentManager.h" + +ComponentManager ComponentManager::mInstance; + +ComponentManager& ComponentManager::GetInstance() { + return mInstance; +} + +ComponentManager::ComponentManager() {} + +void ComponentManager::DeleteAllComponentsOfId(std::uint32_t id) { + for(auto& [type, componentArray] : mComponents) { //Loop through all the types (in the unordered_map<>) + if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + componentArray[id].clear(); //Clear the components at this specific id + } + } +} + +void ComponentManager::DeleteAllComponents() { + mComponents.clear(); //Clear the whole unordered_map<> +} diff --git a/mwe/ecs-memory-efficient/src/Components.cpp b/mwe/ecs-memory-efficient/src/Components.cpp new file mode 100644 index 0000000..69b5eaa --- /dev/null +++ b/mwe/ecs-memory-efficient/src/Components.cpp @@ -0,0 +1,10 @@ +#include "Components.h" +#include + +Component::Component() : mActive(true) {} + +Sprite::Sprite(std::string path) : mPath(path) {} + +Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} + +Colider::Colider(int size) : mSize(size) {} diff --git a/mwe/ecs-memory-efficient/src/GameObjectMax.cpp b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp new file mode 100644 index 0000000..62c41de --- /dev/null +++ b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp @@ -0,0 +1,5 @@ +#include "GameObjectMax.h" + +#include "ComponentManager.h" + +GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, int layer) : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} diff --git a/mwe/ecs-memory-efficient/src/main.cpp b/mwe/ecs-memory-efficient/src/main.cpp new file mode 100644 index 0000000..9e8fbd5 --- /dev/null +++ b/mwe/ecs-memory-efficient/src/main.cpp @@ -0,0 +1,108 @@ +#include +#include +#include + +#include "ComponentManager.h" +#include "GameObjectMax.h" +#include "Components.h" + +int main() { + GameObject gameObect0(0, "Name: 0", "Tag: 0", 0); //Entity 0 + GameObject gameObect1(1, "Name: 1", "Tag: 1", 1); //Entity 1 + GameObject gameObect2(2, "Name: 2", "Tag: 2", 2); //Entity 2 + GameObject gameObect3(3, "Name: 3", "Tag: 3", 3); //Entity 3 + GameObject gameObect4(4, "Name: 4", "Tag: 4", 4); //Entity 4 + GameObject gameObect5(5, "Name: 5", "Tag: 5", 5); //Entity 5 + GameObject gameObect6(6, "Name: 6", "Tag: 6", 6); //Entity 6 + GameObject gameObect7(7, "Name: 7", "Tag: 7", 7); //Entity 7 + + gameObect0.AddComponent("C:/object0"); //Add a sprite to entity0 + gameObect0.AddComponent(1, 2, 3); //Also add a rigidbody to entity0 + gameObect0.AddComponent(3, 2, 1); //Add a second rigidbody to entity0 + + gameObect1.AddComponent(4, 5, 6); //Only add a rigidbody to entity1 + + gameObect2.AddComponent("C:/object2/1"); //Add four sprites to entity2 + gameObect2.AddComponent("C:/object2/2"); + gameObect2.AddComponent("C:/object2/3"); + gameObect2.AddComponent("C:/object2/4"); + gameObect2.AddComponent(10, 100, 500); //Add four rigidbodies to entity2 + gameObect2.AddComponent(10, 100, 501); + gameObect2.AddComponent(10, 100, 502); + gameObect2.AddComponent(10, 100, 500); + + //Add non components to entity3, entity4, entity5 and entity6 + + gameObect7.AddComponent("C:/object7"); //Add a sprite to entity 7 + gameObect7.AddComponent(30); //Add a colder to entity 7 + + //The entities are now initialized + //Now I will demonstrate some ways of retreiving/getting components + + std::cout << "Finding all sprites of entity 0" << std::endl; + std::vector> spriteOfEntity0 = ComponentManager::GetInstance().GetComponentsByID(gameObect0.mId); + for(Sprite& spriteEntity0 : spriteOfEntity0) { + std::cout << "Sprite of entity 0: " << spriteEntity0.mPath << std::endl; + } + std::cout << std::endl; + + std::cout << "Finding all rigidbodies of entity 0" << std::endl; + std::vector> rigidbodyOfEntity0 = ComponentManager::GetInstance().GetComponentsByID(gameObect0.mId); + for(Rigidbody& rigidbodyEntity0 : rigidbodyOfEntity0) { + std::cout << "Rigidbody of entity 0: " << rigidbodyEntity0.mMass << " " << rigidbodyEntity0.mGravityScale << " " << rigidbodyEntity0.mBodyType << std::endl; + + rigidbodyEntity0.mMass = 15; + } + std::cout << std::endl; + + std::cout << "Finding all coliders of entity 0" << std::endl; + std::vector> coliderOfEntity0 = ComponentManager::GetInstance().GetComponentsByID(gameObect0.mId); + for(Colider& coliderEntity0 : coliderOfEntity0) { + std::cout << "Colider of entity 0: " << coliderEntity0.mSize << std::endl; + } + std::cout << std::endl; + + std::cout << "Finding all sprites of entity 3" << std::endl; + std::vector> spriteOfEntity3 = ComponentManager::GetInstance().GetComponentsByID(gameObect3.mId); + for(Sprite& spriteEntity3 : spriteOfEntity3) { + std::cout << "Sprite of entity 3: " << spriteEntity3.mPath << std::endl; + } + std::cout << std::endl; + + std::cout << "Finding all rigidbodies of entity 3" << std::endl; + std::vector> rigidbodyOfEntity3 = ComponentManager::GetInstance().GetComponentsByID(gameObect3.mId); + for(Rigidbody& rigidbodyEntity3 : rigidbodyOfEntity3) { + std::cout << "Rigidbody of entity 3: " << rigidbodyEntity3.mMass << " " << rigidbodyEntity3.mGravityScale << " " << rigidbodyEntity3.mBodyType << std::endl; + } + std::cout << std::endl; + + std::cout << "Finding all sprites of all entities" << std::endl; + std::vector, std::uint32_t>> sprites = ComponentManager::GetInstance().GetComponentsByType(); + for(auto& [sprite, id] : sprites) { + std::cout << "Sprite of id: " << id << ": " << sprite.get().mPath << std::endl; + } + std::cout << std::endl; + + std::cout << "Finding all coliders of all entities" << std::endl; + std::vector, std::uint32_t>> coliders = ComponentManager::GetInstance().GetComponentsByType(); + for(auto& [colider, id] : coliders) { + std::cout << "Colder of id: " << id << ": " << colider.get().mSize << std::endl; + } + std::cout << std::endl; + + std::cout << "Finding all rigidbodies of all entities" << std::endl; + std::vector, std::uint32_t>> rigidBodies = ComponentManager::GetInstance().GetComponentsByType(); + for(auto& [rigidbody, id] : rigidBodies) { + std::cout << "Rigidbody of id: " << id << ": " << rigidbody.get().mMass << " " << rigidbody.get().mGravityScale << " " << rigidbody.get().mBodyType << std::endl; + + rigidbody.get().mMass = -1; + } + std::cout << std::endl; + + std::cout << "Finding all rigidbodies of all entities for the second time (after changing mMass to -1)" << std::endl; + std::vector, std::uint32_t>> rigidBodies2 = ComponentManager::GetInstance().GetComponentsByType(); + for(auto& [rigidbody2, id2] : rigidBodies2) { + std::cout << "Rigidbody of id: " << id2 << ": " << rigidbody2.get().mMass << " " << rigidbody2.get().mGravityScale << " " << rigidbody2.get().mBodyType << std::endl; + } + std::cout << std::endl; +} -- cgit v1.2.3 From c767ef1e3d1d78e50eb1b6d9824b1c8961d68524 Mon Sep 17 00:00:00 2001 From: Max-001 <80035972+Max-001@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:07:18 +0200 Subject: First setup --- .vscode/settings.json | 6 +- mwe/ecs-memory-efficient/inc/ComponentManager.h | 38 +++----- mwe/ecs-memory-efficient/inc/ComponentManager.hpp | 93 ++---------------- mwe/ecs-memory-efficient/inc/ContiguousContainer.h | 34 +++++++ .../inc/ContiguousContainer.hpp | 84 ++++++++++++++++ mwe/ecs-memory-efficient/inc/GameObjectMax.h | 8 +- mwe/ecs-memory-efficient/inc/GameObjectMax.hpp | 16 +++- mwe/ecs-memory-efficient/src/ComponentManager.cpp | 16 ++-- mwe/ecs-memory-efficient/src/main.cpp | 106 +++++---------------- 9 files changed, 199 insertions(+), 202 deletions(-) create mode 100644 mwe/ecs-memory-efficient/inc/ContiguousContainer.h create mode 100644 mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 52c6107..27ae565 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -54,6 +54,10 @@ "cinttypes": "cpp", "typeindex": "cpp", "typeinfo": "cpp", - "variant": "cpp" + "variant": "cpp", + "chrono": "cpp", + "ratio": "cpp", + "iomanip": "cpp", + "sstream": "cpp" } } diff --git a/mwe/ecs-memory-efficient/inc/ComponentManager.h b/mwe/ecs-memory-efficient/inc/ComponentManager.h index 893aa56..066795a 100644 --- a/mwe/ecs-memory-efficient/inc/ComponentManager.h +++ b/mwe/ecs-memory-efficient/inc/ComponentManager.h @@ -1,12 +1,7 @@ #pragma once #include "Components.h" -#include -#include -#include -#include -#include -#include +#include "ContiguousContainer.h" class ComponentManager { public: @@ -17,32 +12,25 @@ public: ComponentManager& operator=(const ComponentManager&) = delete; //Singleton ComponentManager& operator=(ComponentManager&&) = delete; //Singleton - template - void AddComponent(std::uint32_t id, Args&&... args); //Add a component of a specific type - template - void DeleteComponentsById(std::uint32_t id); //Deletes all components of a specific type and id - template - void DeleteComponents(); //Deletes all components of a specific type - void DeleteAllComponentsOfId(std::uint32_t id); //Deletes all components of a specific id - void DeleteAllComponents(); //Deletes all components + template + void addSpriteComponent(Args&&... args); + template + void addRigidbodyComponent(Args&&... args); + template + void addColiderComponent(Args&&... args); - template - std::vector> GetComponentsByID(std::uint32_t id) const; //Get a vector<> of all components at specific type and id - template - std::vector, std::uint32_t>> GetComponentsByType() const; //Get a vector<> of all components of a specific type + std::vector> getAllSpriteReferences(); + std::vector> getAllRigidbodyReferences(); + std::vector> getAllColiderReferences(); private: static ComponentManager mInstance; //Singleton ComponentManager(); //Singleton - /* - * The std::unordered_map>>> below might seem a bit strange, let me explain this structure: - * The std::unordered_map<> has a key and value. The key is a std::type_index and the value is a std::vector. So, a new std::vector will be created for each new std::type_index. - * The first std::vector<> stores another vector<>. This first vector<> is to bind the entity's id to a component. - * The second std::vector<> stores unique_ptrs. Each component can be gathered via an unique_ptr. This second vector<> allows multiple components of the same std::type_index for one entity (id). - */ - std::unordered_map>>> mComponents; + ContiguousContainer mSpriteContainer; + ContiguousContainer mRigidbodyContainer; + ContiguousContainer mColiderContainer; }; #include "ComponentManager.hpp" diff --git a/mwe/ecs-memory-efficient/inc/ComponentManager.hpp b/mwe/ecs-memory-efficient/inc/ComponentManager.hpp index 53dfddd..1607c0c 100644 --- a/mwe/ecs-memory-efficient/inc/ComponentManager.hpp +++ b/mwe/ecs-memory-efficient/inc/ComponentManager.hpp @@ -1,88 +1,15 @@ - -template -void ComponentManager::AddComponent(std::uint32_t id, Args&&... args) { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - if (mComponents.find(type) == mComponents.end()) { //Check if this component type is already in the unordered_map<> - mComponents[type] = std::vector>>(); //If not, create a new (empty) vector<> of vector> - } - - if (id >= mComponents[type].size()) { //Resize the vector<> if the id is greater than the current size - mComponents[type].resize(id + 1); //Initialize new slots to nullptr (resize does automatically init to nullptr) - } - - mComponents[type][id].push_back(std::make_unique(std::forward(args)...)); //Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> -} - -template -void ComponentManager::DeleteComponentsById(std::uint32_t id) { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - std::vector>>& componentArray = mComponents[type]; //Get the correct vector<> - - if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - componentArray[id].clear(); //Clear the whole vector<> of this specific type and id - } - } -} - -template -void ComponentManager::DeleteComponents() { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - mComponents[type].clear(); //Clear the whole vector<> of this specific type - } + +template +void ComponentManager::addSpriteComponent(Args&&... args) { + mSpriteContainer.pushBack(std::forward(args)...); } -template -std::vector> ComponentManager::GetComponentsByID(std::uint32_t id) const { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - std::vector> componentVector; //Create an empty vector<> - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - - const std::vector>>& componentArray = mComponents.at(type); //Get the correct vector<> - - if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - for (const std::unique_ptr& componentPtr : componentArray[id]) { //Loop trough the whole vector<> - T* castedComponent = static_cast(componentPtr.get()); //Cast the unique_ptr to a raw pointer - - if (castedComponent) { //Ensure that the cast was successful - componentVector.push_back(*castedComponent); //Add the dereferenced raw pointer to the vector<> - } - } - } - } - - return componentVector; //Return the vector<> +template +void ComponentManager::addRigidbodyComponent(Args&&... args) { + mRigidbodyContainer.pushBack(std::forward(args)...); } -template -std::vector, std::uint32_t>> ComponentManager::GetComponentsByType() const { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - std::vector, std::uint32_t>> componentVector; //Create an empty vector<> - std::uint32_t id = 0; //Set the id to 0 (the id will also be stored in the returned vector<>) - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - - const std::vector>>& componentArray = mComponents.at(type); //Get the correct vector<> - - for (const std::vector>& component : componentArray) { //Loop through the whole vector<> - for (const std::unique_ptr& componentPtr : component) { //Loop trough the whole vector<> - T* castedComponent = static_cast(componentPtr.get()); //Cast the unique_ptr to a raw pointer - - if (castedComponent) { //Ensure that the cast was successful - componentVector.emplace_back(std::ref(*castedComponent), id); //Pair the dereferenced raw pointer and the id and add it to the vector<> - } - } - - ++id; //Increase the id (the id will also be stored in the returned vector<>) - } - } - - return componentVector; //Return the vector<> +template +void ComponentManager::addColiderComponent(Args&&... args){ + mColiderContainer.pushBack(std::forward(args)...); } diff --git a/mwe/ecs-memory-efficient/inc/ContiguousContainer.h b/mwe/ecs-memory-efficient/inc/ContiguousContainer.h new file mode 100644 index 0000000..dd0321e --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ContiguousContainer.h @@ -0,0 +1,34 @@ +#pragma once + +#include // For malloc and free +#include // For placement new +#include // For std::move and std::forward +#include // For std::bad_alloc +#include // For returning references + +template +class ContiguousContainer { +public: + ContiguousContainer(); + ~ContiguousContainer(); + + // Use perfect forwarding for pushBack + template + void pushBack(Args&&... args); + + void popBack(); + T& operator[](size_t index); + size_t getSize() const; + + // Function to return references to all stored objects + std::vector> getAllReferences(); + +private: + T* mData; + size_t mSize; + size_t mCapacity; + + void resize(size_t new_capacity); // Resize function to allocate more space +}; + +#include "ContiguousContainer.hpp" diff --git a/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp new file mode 100644 index 0000000..878a85f --- /dev/null +++ b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp @@ -0,0 +1,84 @@ +template +ContiguousContainer::ContiguousContainer() + : mSize(0), mCapacity(10) { + // Allocate memory for 10 objects initially + mData = static_cast(malloc(mCapacity * sizeof(T))); + if (!mData) { + throw std::bad_alloc(); + } +} + +template +ContiguousContainer::~ContiguousContainer() { + // Destroy all constructed objects + for (size_t i = 0; i < mSize; ++i) { + mData[i].~T(); + } + // Free the allocated memory + free(mData); +} + +template +template +void ContiguousContainer::pushBack(Args&&... args) { + if (mSize == mCapacity) { + // Double the capacity if the container is full + resize(mCapacity * 2); + } + // Use placement new with perfect forwarding to construct the object in place + new (mData + mSize) T(std::forward(args)...); + ++mSize; +} + +template +void ContiguousContainer::popBack() { + if (mSize > 0) { + --mSize; + // Explicitly call the destructor + mData[mSize].~T(); + } +} + +template +T& ContiguousContainer::operator[](size_t index) { + if (index >= mSize) { + throw std::out_of_range("Index out of range"); + } + return mData[index]; +} + +template +size_t ContiguousContainer::getSize() const { + return mSize; +} + +// Function that returns a vector of references to all stored objects +template +std::vector> ContiguousContainer::getAllReferences() { + std::vector> references; + references.reserve(mSize); // Reserve space to avoid reallocation + for (size_t i = 0; i < mSize; ++i) { + references.push_back(std::ref(mData[i])); + } + return references; +} + +template +void ContiguousContainer::resize(size_t new_capacity) { + // Allocate new memory block with the updated capacity + T* new_data = static_cast(malloc(new_capacity * sizeof(T))); + if (!new_data) { + throw std::bad_alloc(); + } + + // Move or copy existing objects to the new memory block + for (size_t i = 0; i < mSize; ++i) { + new (new_data + i) T(std::move(mData[i])); // Move the objects + mData[i].~T(); // Call the destructor for the old object + } + + // Free the old memory block + free(mData); + mData = new_data; + mCapacity = new_capacity; +} diff --git a/mwe/ecs-memory-efficient/inc/GameObjectMax.h b/mwe/ecs-memory-efficient/inc/GameObjectMax.h index f0bcec9..62cd3e6 100644 --- a/mwe/ecs-memory-efficient/inc/GameObjectMax.h +++ b/mwe/ecs-memory-efficient/inc/GameObjectMax.h @@ -7,8 +7,12 @@ class GameObject { public: GameObject(std::uint32_t id, std::string name, std::string tag, int layer); - template - void AddComponent(Args&&... args); + template + void addSpriteComponent(Args&&... args); + template + void addRigidbodyComponent(Args&&... args); + template + void addColiderComponent(Args&&... args); std::uint32_t mId; std::string mName; diff --git a/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp b/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp index 1e952ba..aac9811 100644 --- a/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp +++ b/mwe/ecs-memory-efficient/inc/GameObjectMax.hpp @@ -1,6 +1,16 @@ #include "ComponentManager.h" -template -void GameObject::AddComponent(Args&&... args) { - ComponentManager::GetInstance().AddComponent(mId, std::forward(args)...); +template +void GameObject::addSpriteComponent(Args&&... args) { + ComponentManager::GetInstance().addSpriteComponent(std::forward(args)...); +} + +template +void GameObject::addRigidbodyComponent(Args&&... args) { + ComponentManager::GetInstance().addRigidbodyComponent(std::forward(args)...); +} + +template +void GameObject::addColiderComponent(Args&&... args) { + ComponentManager::GetInstance().addColiderComponent(std::forward(args)...); } diff --git a/mwe/ecs-memory-efficient/src/ComponentManager.cpp b/mwe/ecs-memory-efficient/src/ComponentManager.cpp index 16cc2b6..20d0ce0 100644 --- a/mwe/ecs-memory-efficient/src/ComponentManager.cpp +++ b/mwe/ecs-memory-efficient/src/ComponentManager.cpp @@ -8,14 +8,14 @@ ComponentManager& ComponentManager::GetInstance() { ComponentManager::ComponentManager() {} -void ComponentManager::DeleteAllComponentsOfId(std::uint32_t id) { - for(auto& [type, componentArray] : mComponents) { //Loop through all the types (in the unordered_map<>) - if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - componentArray[id].clear(); //Clear the components at this specific id - } - } +std::vector> ComponentManager::getAllSpriteReferences() { + return mSpriteContainer.getAllReferences(); } -void ComponentManager::DeleteAllComponents() { - mComponents.clear(); //Clear the whole unordered_map<> +std::vector> ComponentManager::getAllRigidbodyReferences() { + return mRigidbodyContainer.getAllReferences(); +} + +std::vector> ComponentManager::getAllColiderReferences() { + return mColiderContainer.getAllReferences(); } diff --git a/mwe/ecs-memory-efficient/src/main.cpp b/mwe/ecs-memory-efficient/src/main.cpp index 9e8fbd5..71b6b3c 100644 --- a/mwe/ecs-memory-efficient/src/main.cpp +++ b/mwe/ecs-memory-efficient/src/main.cpp @@ -1,108 +1,54 @@ #include #include #include +#include #include "ComponentManager.h" #include "GameObjectMax.h" #include "Components.h" int main() { - GameObject gameObect0(0, "Name: 0", "Tag: 0", 0); //Entity 0 - GameObject gameObect1(1, "Name: 1", "Tag: 1", 1); //Entity 1 - GameObject gameObect2(2, "Name: 2", "Tag: 2", 2); //Entity 2 - GameObject gameObect3(3, "Name: 3", "Tag: 3", 3); //Entity 3 - GameObject gameObect4(4, "Name: 4", "Tag: 4", 4); //Entity 4 - GameObject gameObect5(5, "Name: 5", "Tag: 5", 5); //Entity 5 - GameObject gameObect6(6, "Name: 6", "Tag: 6", 6); //Entity 6 - GameObject gameObect7(7, "Name: 7", "Tag: 7", 7); //Entity 7 + auto startAdding = std::chrono::high_resolution_clock::now(); - gameObect0.AddComponent("C:/object0"); //Add a sprite to entity0 - gameObect0.AddComponent(1, 2, 3); //Also add a rigidbody to entity0 - gameObect0.AddComponent(3, 2, 1); //Add a second rigidbody to entity0 + GameObject* gameObject[1000]; - gameObect1.AddComponent(4, 5, 6); //Only add a rigidbody to entity1 + for(int i = 0; i < 1000; ++i) { + gameObject[i] = new GameObject(i, "Name", "Tag", 0); - gameObect2.AddComponent("C:/object2/1"); //Add four sprites to entity2 - gameObect2.AddComponent("C:/object2/2"); - gameObect2.AddComponent("C:/object2/3"); - gameObect2.AddComponent("C:/object2/4"); - gameObect2.AddComponent(10, 100, 500); //Add four rigidbodies to entity2 - gameObect2.AddComponent(10, 100, 501); - gameObect2.AddComponent(10, 100, 502); - gameObect2.AddComponent(10, 100, 500); - - //Add non components to entity3, entity4, entity5 and entity6 - - gameObect7.AddComponent("C:/object7"); //Add a sprite to entity 7 - gameObect7.AddComponent(30); //Add a colder to entity 7 - - //The entities are now initialized - //Now I will demonstrate some ways of retreiving/getting components - - std::cout << "Finding all sprites of entity 0" << std::endl; - std::vector> spriteOfEntity0 = ComponentManager::GetInstance().GetComponentsByID(gameObect0.mId); - for(Sprite& spriteEntity0 : spriteOfEntity0) { - std::cout << "Sprite of entity 0: " << spriteEntity0.mPath << std::endl; - } - std::cout << std::endl; - - std::cout << "Finding all rigidbodies of entity 0" << std::endl; - std::vector> rigidbodyOfEntity0 = ComponentManager::GetInstance().GetComponentsByID(gameObect0.mId); - for(Rigidbody& rigidbodyEntity0 : rigidbodyOfEntity0) { - std::cout << "Rigidbody of entity 0: " << rigidbodyEntity0.mMass << " " << rigidbodyEntity0.mGravityScale << " " << rigidbodyEntity0.mBodyType << std::endl; - - rigidbodyEntity0.mMass = 15; + gameObject[i]->addSpriteComponent("C:/Test"); + gameObject[i]->addRigidbodyComponent(0, 0, i); + gameObject[i]->addColiderComponent(i); } - std::cout << std::endl; - std::cout << "Finding all coliders of entity 0" << std::endl; - std::vector> coliderOfEntity0 = ComponentManager::GetInstance().GetComponentsByID(gameObect0.mId); - for(Colider& coliderEntity0 : coliderOfEntity0) { - std::cout << "Colider of entity 0: " << coliderEntity0.mSize << std::endl; - } - std::cout << std::endl; + auto stopAdding = std::chrono::high_resolution_clock::now(); - std::cout << "Finding all sprites of entity 3" << std::endl; - std::vector> spriteOfEntity3 = ComponentManager::GetInstance().GetComponentsByID(gameObect3.mId); - for(Sprite& spriteEntity3 : spriteOfEntity3) { - std::cout << "Sprite of entity 3: " << spriteEntity3.mPath << std::endl; - } - std::cout << std::endl; + //This is what systems would do: - std::cout << "Finding all rigidbodies of entity 3" << std::endl; - std::vector> rigidbodyOfEntity3 = ComponentManager::GetInstance().GetComponentsByID(gameObect3.mId); - for(Rigidbody& rigidbodyEntity3 : rigidbodyOfEntity3) { - std::cout << "Rigidbody of entity 3: " << rigidbodyEntity3.mMass << " " << rigidbodyEntity3.mGravityScale << " " << rigidbodyEntity3.mBodyType << std::endl; + std::vector> allSprites = ComponentManager::GetInstance().getAllSpriteReferences(); + for(Sprite& sprite : allSprites) { + std::cout << sprite.mPath << std::endl; } std::cout << std::endl; - std::cout << "Finding all sprites of all entities" << std::endl; - std::vector, std::uint32_t>> sprites = ComponentManager::GetInstance().GetComponentsByType(); - for(auto& [sprite, id] : sprites) { - std::cout << "Sprite of id: " << id << ": " << sprite.get().mPath << std::endl; + std::vector> allRigidbody = ComponentManager::GetInstance().getAllRigidbodyReferences(); + for(Rigidbody& rigidbody : allRigidbody) { + std::cout << rigidbody.mMass << " " << rigidbody.mGravityScale << " " << rigidbody.mBodyType << std::endl; } std::cout << std::endl; - std::cout << "Finding all coliders of all entities" << std::endl; - std::vector, std::uint32_t>> coliders = ComponentManager::GetInstance().GetComponentsByType(); - for(auto& [colider, id] : coliders) { - std::cout << "Colder of id: " << id << ": " << colider.get().mSize << std::endl; + std::vector> allColider = ComponentManager::GetInstance().getAllColiderReferences(); + for(Colider& colider : allColider) { + std::cout << colider.mSize << std::endl; } - std::cout << std::endl; - std::cout << "Finding all rigidbodies of all entities" << std::endl; - std::vector, std::uint32_t>> rigidBodies = ComponentManager::GetInstance().GetComponentsByType(); - for(auto& [rigidbody, id] : rigidBodies) { - std::cout << "Rigidbody of id: " << id << ": " << rigidbody.get().mMass << " " << rigidbody.get().mGravityScale << " " << rigidbody.get().mBodyType << std::endl; + auto stopLooping = std::chrono::high_resolution_clock::now(); - rigidbody.get().mMass = -1; + for (int i = 0; i < 1000; ++i) { + delete gameObject[i]; } - std::cout << std::endl; - std::cout << "Finding all rigidbodies of all entities for the second time (after changing mMass to -1)" << std::endl; - std::vector, std::uint32_t>> rigidBodies2 = ComponentManager::GetInstance().GetComponentsByType(); - for(auto& [rigidbody2, id2] : rigidBodies2) { - std::cout << "Rigidbody of id: " << id2 << ": " << rigidbody2.get().mMass << " " << rigidbody2.get().mGravityScale << " " << rigidbody2.get().mBodyType << std::endl; - } - std::cout << std::endl; + auto Addtime = std::chrono::duration_cast(stopAdding - startAdding); + auto LoopTime = std::chrono::duration_cast(stopLooping - stopAdding); + std::cout << "AddTime: " << Addtime.count() << " us" << std::endl; + std::cout << "LoopTime: " << LoopTime.count() << " us" << std::endl; } -- cgit v1.2.3 From 809db83cd515c6c2b1d404811354208cf97a5c07 Mon Sep 17 00:00:00 2001 From: Max-001 <80035972+Max-001@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:28:58 +0200 Subject: Improved test --- mwe/ecs-memory-efficient/src/main.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mwe/ecs-memory-efficient/src/main.cpp b/mwe/ecs-memory-efficient/src/main.cpp index 71b6b3c..c25816b 100644 --- a/mwe/ecs-memory-efficient/src/main.cpp +++ b/mwe/ecs-memory-efficient/src/main.cpp @@ -10,9 +10,9 @@ int main() { auto startAdding = std::chrono::high_resolution_clock::now(); - GameObject* gameObject[1000]; + GameObject* gameObject[100000]; - for(int i = 0; i < 1000; ++i) { + for(int i = 0; i < 100000; ++i) { gameObject[i] = new GameObject(i, "Name", "Tag", 0); gameObject[i]->addSpriteComponent("C:/Test"); @@ -26,24 +26,24 @@ int main() { std::vector> allSprites = ComponentManager::GetInstance().getAllSpriteReferences(); for(Sprite& sprite : allSprites) { - std::cout << sprite.mPath << std::endl; + //std::cout << sprite.mPath << std::endl; } - std::cout << std::endl; + //std::cout << std::endl; std::vector> allRigidbody = ComponentManager::GetInstance().getAllRigidbodyReferences(); for(Rigidbody& rigidbody : allRigidbody) { - std::cout << rigidbody.mMass << " " << rigidbody.mGravityScale << " " << rigidbody.mBodyType << std::endl; + //std::cout << rigidbody.mMass << " " << rigidbody.mGravityScale << " " << rigidbody.mBodyType << std::endl; } - std::cout << std::endl; + //std::cout << std::endl; std::vector> allColider = ComponentManager::GetInstance().getAllColiderReferences(); for(Colider& colider : allColider) { - std::cout << colider.mSize << std::endl; + //std::cout << colider.mSize << std::endl; } auto stopLooping = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < 1000; ++i) { + for (int i = 0; i < 100000; ++i) { delete gameObject[i]; } -- cgit v1.2.3 From 094ce1806156e191ffb554d4e88e636421cb0242 Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Mon, 7 Oct 2024 10:04:23 +0200 Subject: particle direction is random --- src/crepe/CMakeLists.txt | 1 + src/crepe/ParticleEmitter.cpp | 70 +++++++++++++++++++++++++----------- src/crepe/ParticleEmitter.hpp | 32 +++++++++-------- src/crepe/SDLApp.cpp | 69 ++++++++++++++++++++++++++++++++++++ src/crepe/SDLApp.hpp | 25 +++++++++++++ src/crepe/main.cpp | 82 ++++++++++++++----------------------------- 6 files changed, 188 insertions(+), 91 deletions(-) create mode 100644 src/crepe/SDLApp.cpp create mode 100644 src/crepe/SDLApp.hpp diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index c825529..933f907 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -2,6 +2,7 @@ target_sources(main PUBLIC main.cpp Particle.cpp ParticleEmitter.cpp + SDLApp.cpp ) target_include_directories(main PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/crepe/ParticleEmitter.cpp b/src/crepe/ParticleEmitter.cpp index 13aaeae..f575e38 100644 --- a/src/crepe/ParticleEmitter.cpp +++ b/src/crepe/ParticleEmitter.cpp @@ -1,40 +1,70 @@ #include "ParticleEmitter.hpp" +#include +#include +#include +#include // include iostream for std::cout -ParticleEmitter::ParticleEmitter(int maxParticles, float emissionRate) - : maxParticles(maxParticles), emissionRate(emissionRate), elapsedTime(0.0f) { - position = { 0, 0 }; - std::srand(static_cast(std::time(nullptr))); / +ParticleEmitter::ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset) + : m_maxParticles(maxParticles), m_emissionRate(emissionRate), m_elapsedTime(0.0f), m_speed(speed), m_speedOffset(speedOffset), m_position{0, 0} { + std::srand(static_cast(std::time(nullptr))); // initialize random seed + + m_minAngle = (360 + angle - (angleOffset % 360)) % 360; // calculate minAngle + m_maxAngle = (360 + angle + (angleOffset % 360)) % 360; // calculate maxAngle + + std::cout << "Initialized ParticleEmitter with:" << std::endl; + std::cout << "Min Angle: " << m_minAngle << " Max Angle: " << m_maxAngle << std::endl; } -void ParticleEmitter::update(float deltaTime) { - elapsedTime += deltaTime; +void ParticleEmitter::update(float deltaTime) { // keep deltaTime as float + m_elapsedTime += deltaTime; - while (elapsedTime >= (1.0f / emissionRate) && particles.size() < maxParticles) { - elapsedTime -= (1.0f / emissionRate); + while (m_elapsedTime >= (1.0f / m_emissionRate) && particles.size() < m_maxParticles) { + m_elapsedTime -= (1.0f / m_emissionRate); emitParticle(); } - for (auto it = particles.begin(); it != particles.end();) { - it->update(deltaTime); + std::vector::iterator it = particles.begin(); + while (it != particles.end()) { + it->update(deltaTime); // update particle if (!it->isAlive()) { - it = particles.erase(it); + it = particles.erase(it); // remove dead particle } else { - ++it; + ++it; // move to next particle } } } void ParticleEmitter::emitParticle() { - Particle::Position initialPosition = { position.x, position.y }; + Particle::Position initialPosition = { m_position.x, m_position.y }; // starting position of the particle + + // generate a random angle within the range [minAngle, maxAngle] + float randomAngle = m_minAngle + (std::rand() % (static_cast(m_maxAngle - m_minAngle + 1))); // random angle within the specified range + + // convert angle to radians for velocity calculation + float angleInRadians = randomAngle * (M_PI / 180.0f); + + // calculate velocity components based on the final angle + float randomSpeedOffset = (static_cast(std::rand()) / RAND_MAX) * (2 * m_speedOffset) - m_speedOffset; // random speed offset + float velocityX = (m_speed + randomSpeedOffset) * std::cos(angleInRadians); // x component of velocity + float velocityY = (m_speed + randomSpeedOffset) * std::sin(angleInRadians); // y component of velocity + Particle::Position initialVelocity = { - static_cast(randFloat(-50.0f, 50.0f, 10.0f, 100.0f)), - static_cast(randFloat(-50.0f, 50.0f, 10.0f, 100.0f)) + static_cast(velocityX), // set x velocity + static_cast(velocityY) // set y velocity }; - particles.emplace_back(2.0f, initialPosition, initialVelocity); + + std::cout << "Emitting particle with:" << std::endl; + std::cout << "Random Angle: " << randomAngle << " (Radians: " << angleInRadians << ")" << std::endl; + std::cout << "Velocity X: " << velocityX << " Velocity Y: " << velocityY << std::endl; + + particles.emplace_back(2.0f, initialPosition, initialVelocity); // create and store the new particle +} + +const std::vector& ParticleEmitter::getParticles() const { + return particles; // return the collection of particles } -float ParticleEmitter::randFloat(float minangle, float maxangle, float minVel, float maxVel) { - float angle = static_cast(rand()) / RAND_MAX * (maxangle - minangle) + minangle; - float velocity = static_cast(rand()) / RAND_MAX * (maxVel - minVel) + minVel; - return velocity * std::cos(angle); +void ParticleEmitter::setPosition(int x, int y) { + m_position.x = x; // set x position of the emitter + m_position.y = y; // set y position of the emitter } diff --git a/src/crepe/ParticleEmitter.hpp b/src/crepe/ParticleEmitter.hpp index 682a2ae..9b3f2ad 100644 --- a/src/crepe/ParticleEmitter.hpp +++ b/src/crepe/ParticleEmitter.hpp @@ -1,28 +1,30 @@ #pragma once #include -#include -#include #include "Particle.hpp" -#include class ParticleEmitter { public: - std::vector particles; + ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset); + void update(float deltaTime); // Keep deltaTime as float + const std::vector& getParticles() const; //returns the collection of particles + void setPosition(int x, int y); //sets the position of the emitter +private: + void emitParticle(); //emits a new particle - struct Position { + struct Position { //struct to hold position int x; int y; - } position; - - int maxParticles; - float emissionRate; - float elapsedTime; + }; - ParticleEmitter(int maxParticles, float emissionRate); - void update(float deltaTime); + Position m_position; //position of the emitter + unsigned int m_maxParticles; //maximum number of particles + unsigned int m_emissionRate; //rate of particle emission + float m_elapsedTime; //elapsed time since the last emission + unsigned int m_speed; //base speed of the particles + unsigned int m_speedOffset; //offset for random speed variation + unsigned int m_minAngle; //min angle of particle emission + unsigned int m_maxAngle; //max angle of particle emission -private: - void emitParticle(); - float randFloat(float minangle, float maxangle, float minVel, float maxVel); + std::vector particles; //collection of particles }; diff --git a/src/crepe/SDLApp.cpp b/src/crepe/SDLApp.cpp new file mode 100644 index 0000000..a5e3621 --- /dev/null +++ b/src/crepe/SDLApp.cpp @@ -0,0 +1,69 @@ +#include "SDLApp.hpp" +#include + +SDLApp::SDLApp(int windowWidth, int windowHeight) + : windowWidth(windowWidth), windowHeight(windowHeight), window(nullptr), renderer(nullptr) {} + +SDLApp::~SDLApp() { + cleanUp(); +} + +bool SDLApp::initialize() { + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + std::cerr << "SDL Initialization Error: " << SDL_GetError() << std::endl; + return false; + } + + window = SDL_CreateWindow("Particle System", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + windowWidth, + windowHeight, + SDL_WINDOW_SHOWN); + if (!window) { + std::cerr << "Window Creation Error: " << SDL_GetError() << std::endl; + return false; + } + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!renderer) { + std::cerr << "Renderer Creation Error: " << SDL_GetError() << std::endl; + return false; + } + + return true; +} + +void SDLApp::handleEvents(bool& running) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + running = false; + } + } +} + +void SDLApp::clearScreen() { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); +} + +void SDLApp::presentScreen() { + SDL_RenderPresent(renderer); +} + +void SDLApp::drawSquare(int x, int y, int size) { + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + SDL_Rect rect = { x, y, size, size }; + SDL_RenderFillRect(renderer, &rect); +} + +void SDLApp::cleanUp() { + if (renderer) { + SDL_DestroyRenderer(renderer); + } + if (window) { + SDL_DestroyWindow(window); + } + SDL_Quit(); +} diff --git a/src/crepe/SDLApp.hpp b/src/crepe/SDLApp.hpp new file mode 100644 index 0000000..92a4e27 --- /dev/null +++ b/src/crepe/SDLApp.hpp @@ -0,0 +1,25 @@ +#ifndef SDLAPP_HPP +#define SDLAPP_HPP + +#include + +class SDLApp { +public: + SDLApp(int windowWidth, int windowHeight); + ~SDLApp(); + + bool initialize(); + void handleEvents(bool& running); + void clearScreen(); + void presentScreen(); + void drawSquare(int x, int y, int size); + void cleanUp(); + +private: + int windowWidth; + int windowHeight; + SDL_Window* window; + SDL_Renderer* renderer; +}; + +#endif diff --git a/src/crepe/main.cpp b/src/crepe/main.cpp index 73d041c..fc307cc 100644 --- a/src/crepe/main.cpp +++ b/src/crepe/main.cpp @@ -1,83 +1,53 @@ #include #include #include -#include +#include "SDLApp.hpp" #include "ParticleEmitter.hpp" const int WINDOW_WIDTH = 800; const int WINDOW_HEIGHT = 600; -void renderParticles(SDL_Renderer* renderer, const ParticleEmitter& emitter) { - for (const auto& particle : emitter.particles) { - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_Rect rect = { particle.position.x, particle.position.y, 5, 5 }; - SDL_RenderFillRect(renderer, &rect); - } -} - int main(int argc, char* argv[]) { - - if (SDL_Init(SDL_INIT_VIDEO) != 0) { - std::cerr << "SDL Initialization Error: " << SDL_GetError() << std::endl; - return 1; - } + SDLApp app(WINDOW_WIDTH, WINDOW_HEIGHT); - SDL_Window* window = SDL_CreateWindow("Particle System", - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - WINDOW_WIDTH, - WINDOW_HEIGHT, - SDL_WINDOW_SHOWN); - if (!window) { - std::cerr << "Window Creation Error: " << SDL_GetError() << std::endl; - SDL_Quit(); + if (!app.initialize()) { + std::cerr << "Failed to initialize SDLApp." << std::endl; return 1; } - // Create SDL renderer - SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - if (!renderer) { - std::cerr << "Renderer Creation Error: " << SDL_GetError() << std::endl; - SDL_DestroyWindow(window); - SDL_Quit(); - return 1; - } - ParticleEmitter emitter(100, 10.0f); - emitter.position = { 400, 300 }; - ParticleEmitter emitter2(100, 10.0f); - emitter2.position = { 600, 600 }; + unsigned int maxParticles = 100; //maximum number of particles + unsigned int emissionRate = 10; //particles created per second + unsigned int speed = 50; //base speed of particles + unsigned int speedOffset = 10; //random offset for particle speed + unsigned int angle = 180; //base angle of particle emission + unsigned int angleOffset = 30; //random offset for particle angle - float deltaTime = 0.1f; - bool running = true; - SDL_Event event; - while (running) { + ParticleEmitter emitter(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset); - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { - running = false; - } - } + emitter.setPosition(400, 300); + + float deltaTime = 0.1f; + bool running = true; + while (running) { + app.handleEvents(running); emitter.update(deltaTime); - emitter2.update(deltaTime); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderClear(renderer); + app.clearScreen(); - renderParticles(renderer, emitter); - renderParticles(renderer, emitter2); + //render particles using the drawSquare method + const std::vector& particles = emitter.getParticles(); + for (std::size_t i = 0; i < particles.size(); ++i) { + app.drawSquare(particles[i].position.x, particles[i].position.y, 5); + } - SDL_RenderPresent(renderer); + app.presentScreen(); - + std::this_thread::sleep_for(std::chrono::milliseconds(17)); } - - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); - + app.cleanUp(); return 0; } -- cgit v1.2.3 From 096e0d0a7199ec9a4059fd5ef8a06a3cf8fcae83 Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Mon, 7 Oct 2024 16:02:09 +0200 Subject: created a particel system --- src/crepe/CMakeLists.txt | 1 + src/crepe/Particle.cpp | 5 ++-- src/crepe/Particle.hpp | 5 ++-- src/crepe/ParticleEmitter.cpp | 63 ++----------------------------------------- src/crepe/ParticleEmitter.hpp | 11 ++------ src/crepe/ParticleSystem.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++ src/crepe/ParticleSystem.hpp | 14 ++++++++++ src/crepe/main.cpp | 37 +++++++++++++++---------- 8 files changed, 109 insertions(+), 89 deletions(-) create mode 100644 src/crepe/ParticleSystem.cpp create mode 100644 src/crepe/ParticleSystem.hpp diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 933f907..43a3835 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -2,6 +2,7 @@ target_sources(main PUBLIC main.cpp Particle.cpp ParticleEmitter.cpp + ParticleSystem.cpp SDLApp.cpp ) diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp index 90957db..f6bbd34 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -1,12 +1,13 @@ #include "Particle.hpp" + Particle::Particle(float lifespan, Position position, Position velocity) : lifespan(lifespan), position(position), velocity(velocity), timeInLife(0.0f) {} void Particle::update(float deltaTime) { timeInLife += deltaTime; - position.x += static_cast(velocity.x * deltaTime); - position.y += static_cast(velocity.y * deltaTime); + position.x += velocity.x * deltaTime; + position.y += velocity.y * deltaTime; } bool Particle::isAlive() const { diff --git a/src/crepe/Particle.hpp b/src/crepe/Particle.hpp index f8d2770..f71fd67 100644 --- a/src/crepe/Particle.hpp +++ b/src/crepe/Particle.hpp @@ -3,8 +3,8 @@ class Particle { public: struct Position { - int x; - int y; + float x; + float y; }; Position position; @@ -14,6 +14,5 @@ public: Particle(float lifespan, Position position, Position velocity); void update(float deltaTime); bool isAlive() const; -private: float timeInLife; }; diff --git a/src/crepe/ParticleEmitter.cpp b/src/crepe/ParticleEmitter.cpp index f575e38..69f5ace 100644 --- a/src/crepe/ParticleEmitter.cpp +++ b/src/crepe/ParticleEmitter.cpp @@ -1,70 +1,11 @@ #include "ParticleEmitter.hpp" -#include -#include #include -#include // include iostream for std::cout ParticleEmitter::ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset) - : m_maxParticles(maxParticles), m_emissionRate(emissionRate), m_elapsedTime(0.0f), m_speed(speed), m_speedOffset(speedOffset), m_position{0, 0} { + : m_maxParticles(maxParticles), m_emissionRate(emissionRate), m_speed(speed), m_speedOffset(speedOffset), m_position{0, 0} { std::srand(static_cast(std::time(nullptr))); // initialize random seed m_minAngle = (360 + angle - (angleOffset % 360)) % 360; // calculate minAngle m_maxAngle = (360 + angle + (angleOffset % 360)) % 360; // calculate maxAngle - std::cout << "Initialized ParticleEmitter with:" << std::endl; - std::cout << "Min Angle: " << m_minAngle << " Max Angle: " << m_maxAngle << std::endl; -} - -void ParticleEmitter::update(float deltaTime) { // keep deltaTime as float - m_elapsedTime += deltaTime; - - while (m_elapsedTime >= (1.0f / m_emissionRate) && particles.size() < m_maxParticles) { - m_elapsedTime -= (1.0f / m_emissionRate); - emitParticle(); - } - - std::vector::iterator it = particles.begin(); - while (it != particles.end()) { - it->update(deltaTime); // update particle - if (!it->isAlive()) { - it = particles.erase(it); // remove dead particle - } else { - ++it; // move to next particle - } - } -} - -void ParticleEmitter::emitParticle() { - Particle::Position initialPosition = { m_position.x, m_position.y }; // starting position of the particle - - // generate a random angle within the range [minAngle, maxAngle] - float randomAngle = m_minAngle + (std::rand() % (static_cast(m_maxAngle - m_minAngle + 1))); // random angle within the specified range - - // convert angle to radians for velocity calculation - float angleInRadians = randomAngle * (M_PI / 180.0f); - - // calculate velocity components based on the final angle - float randomSpeedOffset = (static_cast(std::rand()) / RAND_MAX) * (2 * m_speedOffset) - m_speedOffset; // random speed offset - float velocityX = (m_speed + randomSpeedOffset) * std::cos(angleInRadians); // x component of velocity - float velocityY = (m_speed + randomSpeedOffset) * std::sin(angleInRadians); // y component of velocity - - Particle::Position initialVelocity = { - static_cast(velocityX), // set x velocity - static_cast(velocityY) // set y velocity - }; - - std::cout << "Emitting particle with:" << std::endl; - std::cout << "Random Angle: " << randomAngle << " (Radians: " << angleInRadians << ")" << std::endl; - std::cout << "Velocity X: " << velocityX << " Velocity Y: " << velocityY << std::endl; - - particles.emplace_back(2.0f, initialPosition, initialVelocity); // create and store the new particle -} - -const std::vector& ParticleEmitter::getParticles() const { - return particles; // return the collection of particles -} - -void ParticleEmitter::setPosition(int x, int y) { - m_position.x = x; // set x position of the emitter - m_position.y = y; // set y position of the emitter -} +} \ No newline at end of file diff --git a/src/crepe/ParticleEmitter.hpp b/src/crepe/ParticleEmitter.hpp index 9b3f2ad..6826531 100644 --- a/src/crepe/ParticleEmitter.hpp +++ b/src/crepe/ParticleEmitter.hpp @@ -6,21 +6,14 @@ class ParticleEmitter { public: ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset); - void update(float deltaTime); // Keep deltaTime as float - const std::vector& getParticles() const; //returns the collection of particles - void setPosition(int x, int y); //sets the position of the emitter -private: - void emitParticle(); //emits a new particle - struct Position { //struct to hold position - int x; - int y; + float x; + float y; }; Position m_position; //position of the emitter unsigned int m_maxParticles; //maximum number of particles unsigned int m_emissionRate; //rate of particle emission - float m_elapsedTime; //elapsed time since the last emission unsigned int m_speed; //base speed of the particles unsigned int m_speedOffset; //offset for random speed variation unsigned int m_minAngle; //min angle of particle emission diff --git a/src/crepe/ParticleSystem.cpp b/src/crepe/ParticleSystem.cpp new file mode 100644 index 0000000..367f56c --- /dev/null +++ b/src/crepe/ParticleSystem.cpp @@ -0,0 +1,62 @@ +#include "ParticleSystem.hpp" +#include +// #include +#include +#include // include iostream for std::cout +#include "ParticleEmitter.hpp" +#include "Particle.hpp" + +ParticleSystem::ParticleSystem() : m_elapsedTime(0.0f) {} // Initialize m_elapsedTime to 0 + +void ParticleSystem::update(float deltaTime, std::vector& emitters) { + for (ParticleEmitter& emitter : emitters) { + m_elapsedTime = deltaTime; + + //spawn new particles if enough time passed and the max is not achieved + while (m_elapsedTime >= (1.0f / emitter.m_emissionRate) && emitter.particles.size() < emitter.m_maxParticles) { + m_elapsedTime -= (1.0f / emitter.m_emissionRate); + emitParticle(emitter); + } + //update/move particles afterwards delete if not alive. + std::vector::iterator it = emitter.particles.begin(); + while (it != emitter.particles.end()) { + it->update(deltaTime); + if (!it->isAlive()) { + it = emitter.particles.erase(it); + } else { + ++it; + } + } + + } +} + +void ParticleSystem::emitParticle(ParticleEmitter& emitter) { + std::cout << "new emitter:" << std::endl; + Particle::Position initialPosition = { emitter.m_position.x, emitter.m_position.y }; + float randomAngle = 0.0f; + //check if value is overthe 360 degrees + if(emitter.m_maxAngle < emitter.m_minAngle) + { + randomAngle = ((emitter.m_minAngle + (std::rand() % (static_cast(emitter.m_maxAngle + 360 - emitter.m_minAngle + 1))))%360); + } + else + { + randomAngle = emitter.m_minAngle + (std::rand() % (static_cast(emitter.m_maxAngle - emitter.m_minAngle + 1))); + } + + std::cout << "randomAngle:" << randomAngle << std::endl; + float angleInRadians = randomAngle * (M_PI / 180.0f); + float randomSpeedOffset = (static_cast(std::rand()) / RAND_MAX) * (2 * emitter.m_speedOffset) - emitter.m_speedOffset; + float velocityX = (emitter.m_speed + randomSpeedOffset) * std::cos(angleInRadians); + float velocityY = (emitter.m_speed + randomSpeedOffset) * std::sin(angleInRadians); + std::cout << "velocityX:" << velocityX << std::endl; + std::cout << "velocityY:" << velocityY << std::endl; + Particle::Position initialVelocity = { + velocityX, + velocityY + }; + std::cout << "initialVelocityX:" << initialVelocity.x << std::endl; + std::cout << "initialVelocityY:" << initialVelocity.y << std::endl; + emitter.particles.emplace_back(2.0f, initialPosition, initialVelocity); +} diff --git a/src/crepe/ParticleSystem.hpp b/src/crepe/ParticleSystem.hpp new file mode 100644 index 0000000..f670415 --- /dev/null +++ b/src/crepe/ParticleSystem.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "ParticleEmitter.hpp" + +class ParticleSystem { +public: + ParticleSystem(); + void update(float deltaTime, std::vector& emitters); +private: + void emitParticle(ParticleEmitter &emitter); //emits a new particle + + float m_elapsedTime; //elapsed time since the last emission +}; diff --git a/src/crepe/main.cpp b/src/crepe/main.cpp index fc307cc..40eb04f 100644 --- a/src/crepe/main.cpp +++ b/src/crepe/main.cpp @@ -3,6 +3,8 @@ #include #include "SDLApp.hpp" #include "ParticleEmitter.hpp" +#include "ParticleSystem.hpp" +#include "Particle.hpp" const int WINDOW_WIDTH = 800; const int WINDOW_HEIGHT = 600; @@ -15,37 +17,44 @@ int main(int argc, char* argv[]) { return 1; } + ParticleSystem particleSystem; - unsigned int maxParticles = 100; //maximum number of particles - unsigned int emissionRate = 10; //particles created per second - unsigned int speed = 50; //base speed of particles - unsigned int speedOffset = 10; //random offset for particle speed - unsigned int angle = 180; //base angle of particle emission - unsigned int angleOffset = 30; //random offset for particle angle + unsigned int maxParticles = 100; // maximum number of particles + unsigned int emissionRate = 10; // particles created per second + unsigned int speed = 50; // base speed of particles + unsigned int speedOffset = 10; // random offset for particle speed + unsigned int angle = 90; // base angle of particle emission + unsigned int angleOffset = 30; // random offset for particle angle + ParticleEmitter emitter1(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset); + emitter1.m_position = {200, 200}; // set the position of the first emitter - ParticleEmitter emitter(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset); + ParticleEmitter emitter2(maxParticles, emissionRate, speed, speedOffset, angle - 90, angleOffset); // Another emitter + emitter2.m_position = {200, 150}; // set the position of the second emitter - emitter.setPosition(400, 300); + std::vector emitters = { emitter2 }; // array of emitters float deltaTime = 0.1f; bool running = true; while (running) { app.handleEvents(running); - emitter.update(deltaTime); + + particleSystem.update(deltaTime, emitters); // update particle system with delta time and emitters app.clearScreen(); - //render particles using the drawSquare method - const std::vector& particles = emitter.getParticles(); - for (std::size_t i = 0; i < particles.size(); ++i) { - app.drawSquare(particles[i].position.x, particles[i].position.y, 5); + // render particles using the drawSquare method from SDLApp + for (const ParticleEmitter& emitter : emitters) { + for (const Particle& particle : emitter.particles) { + + app.drawSquare(particle.position.x, particle.position.y, 5); // draw each particle + } } app.presentScreen(); - std::this_thread::sleep_for(std::chrono::milliseconds(17)); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); // simulate ~50 FPS } app.cleanUp(); -- cgit v1.2.3 From 5cf5cf64d6f9597849ed5558da4f1bc201165fed Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Mon, 7 Oct 2024 16:39:26 +0200 Subject: updated system --- src/crepe/Particle.cpp | 3 ++- src/crepe/ParticleEmitter.cpp | 4 ++-- src/crepe/ParticleEmitter.hpp | 4 +++- src/crepe/ParticleSystem.cpp | 21 +++++++++------------ src/crepe/main.cpp | 37 ++++++++++++++++++++++++++----------- 5 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp index f6bbd34..3254b5b 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -1,5 +1,5 @@ #include "Particle.hpp" - +#include Particle::Particle(float lifespan, Position position, Position velocity) : lifespan(lifespan), position(position), velocity(velocity), timeInLife(0.0f) {} @@ -11,5 +11,6 @@ void Particle::update(float deltaTime) { } bool Particle::isAlive() const { + std::cout << "lifespan" << lifespan << std::endl; return timeInLife < lifespan; } diff --git a/src/crepe/ParticleEmitter.cpp b/src/crepe/ParticleEmitter.cpp index 69f5ace..f5c18db 100644 --- a/src/crepe/ParticleEmitter.cpp +++ b/src/crepe/ParticleEmitter.cpp @@ -1,8 +1,8 @@ #include "ParticleEmitter.hpp" #include -ParticleEmitter::ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset) - : m_maxParticles(maxParticles), m_emissionRate(emissionRate), m_speed(speed), m_speedOffset(speedOffset), m_position{0, 0} { +ParticleEmitter::ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset, float m_beginLifespan, float m_endLifespan) + : m_maxParticles(maxParticles), m_emissionRate(emissionRate), m_speed(speed), m_speedOffset(speedOffset), m_position{0, 0}, m_beginLifespan(m_beginLifespan),m_endLifespan(m_endLifespan) { std::srand(static_cast(std::time(nullptr))); // initialize random seed m_minAngle = (360 + angle - (angleOffset % 360)) % 360; // calculate minAngle diff --git a/src/crepe/ParticleEmitter.hpp b/src/crepe/ParticleEmitter.hpp index 6826531..f647c27 100644 --- a/src/crepe/ParticleEmitter.hpp +++ b/src/crepe/ParticleEmitter.hpp @@ -5,7 +5,7 @@ class ParticleEmitter { public: - ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset); + ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset,float m_beginLifespan,float m_endLifespan); struct Position { //struct to hold position float x; float y; @@ -18,6 +18,8 @@ public: unsigned int m_speedOffset; //offset for random speed variation unsigned int m_minAngle; //min angle of particle emission unsigned int m_maxAngle; //max angle of particle emission + float m_beginLifespan; //begin Lifespan of particle (only visual) + float m_endLifespan; //begin Lifespan of particle std::vector particles; //collection of particles }; diff --git a/src/crepe/ParticleSystem.cpp b/src/crepe/ParticleSystem.cpp index 367f56c..f3cb939 100644 --- a/src/crepe/ParticleSystem.cpp +++ b/src/crepe/ParticleSystem.cpp @@ -10,13 +10,12 @@ ParticleSystem::ParticleSystem() : m_elapsedTime(0.0f) {} // Initialize m_elaps void ParticleSystem::update(float deltaTime, std::vector& emitters) { for (ParticleEmitter& emitter : emitters) { - m_elapsedTime = deltaTime; - - //spawn new particles if enough time passed and the max is not achieved - while (m_elapsedTime >= (1.0f / emitter.m_emissionRate) && emitter.particles.size() < emitter.m_maxParticles) { - m_elapsedTime -= (1.0f / emitter.m_emissionRate); + float updateAmount = 1/static_cast(emitter.m_emissionRate); + for (float i = 0; i < deltaTime; i += updateAmount) + { emitParticle(emitter); } + //update/move particles afterwards delete if not alive. std::vector::iterator it = emitter.particles.begin(); while (it != emitter.particles.end()) { @@ -32,7 +31,7 @@ void ParticleSystem::update(float deltaTime, std::vector& emitt } void ParticleSystem::emitParticle(ParticleEmitter& emitter) { - std::cout << "new emitter:" << std::endl; + // std::cout << "new emitter:" << std::endl; Particle::Position initialPosition = { emitter.m_position.x, emitter.m_position.y }; float randomAngle = 0.0f; //check if value is overthe 360 degrees @@ -45,18 +44,16 @@ void ParticleSystem::emitParticle(ParticleEmitter& emitter) { randomAngle = emitter.m_minAngle + (std::rand() % (static_cast(emitter.m_maxAngle - emitter.m_minAngle + 1))); } - std::cout << "randomAngle:" << randomAngle << std::endl; + float angleInRadians = randomAngle * (M_PI / 180.0f); float randomSpeedOffset = (static_cast(std::rand()) / RAND_MAX) * (2 * emitter.m_speedOffset) - emitter.m_speedOffset; float velocityX = (emitter.m_speed + randomSpeedOffset) * std::cos(angleInRadians); float velocityY = (emitter.m_speed + randomSpeedOffset) * std::sin(angleInRadians); - std::cout << "velocityX:" << velocityX << std::endl; - std::cout << "velocityY:" << velocityY << std::endl; Particle::Position initialVelocity = { velocityX, velocityY }; - std::cout << "initialVelocityX:" << initialVelocity.x << std::endl; - std::cout << "initialVelocityY:" << initialVelocity.y << std::endl; - emitter.particles.emplace_back(2.0f, initialPosition, initialVelocity); + // std::cout << "emitter.m_endLifespan:" << emitter.m_endLifespan << std::endl; + + emitter.particles.emplace_back(emitter.m_endLifespan, initialPosition, initialVelocity); } diff --git a/src/crepe/main.cpp b/src/crepe/main.cpp index 40eb04f..314dbed 100644 --- a/src/crepe/main.cpp +++ b/src/crepe/main.cpp @@ -5,6 +5,7 @@ #include "ParticleEmitter.hpp" #include "ParticleSystem.hpp" #include "Particle.hpp" +#include const int WINDOW_WIDTH = 800; const int WINDOW_HEIGHT = 600; @@ -19,29 +20,43 @@ int main(int argc, char* argv[]) { ParticleSystem particleSystem; - unsigned int maxParticles = 100; // maximum number of particles - unsigned int emissionRate = 10; // particles created per second + unsigned int maxParticles = 10; // maximum number of particles + unsigned int emissionRate = 1; // particles created per second unsigned int speed = 50; // base speed of particles unsigned int speedOffset = 10; // random offset for particle speed unsigned int angle = 90; // base angle of particle emission unsigned int angleOffset = 30; // random offset for particle angle - - ParticleEmitter emitter1(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset); - emitter1.m_position = {200, 200}; // set the position of the first emitter - - ParticleEmitter emitter2(maxParticles, emissionRate, speed, speedOffset, angle - 90, angleOffset); // Another emitter - emitter2.m_position = {200, 150}; // set the position of the second emitter - - std::vector emitters = { emitter2 }; // array of emitters - + float beginLifespan = 0.0f; // beginning lifespan of particles + float endLifespan = 2.0f; // ending lifespan of particles + + // Vector to hold all the emitters + std::vector emitters; + + // Loop to create 1000 emitters + for (unsigned int i = 0; i < 100; ++i) { + ParticleEmitter emitter(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset, beginLifespan, endLifespan); + + // Set a position for each emitter, modifying the position for demonstration + emitter.m_position = {static_cast(200 + (i % 100)), static_cast(200 + (i / 100) * 10)}; // Adjust position for each emitter + + emitters.push_back(emitter); // Add the emitter to the vector + } float deltaTime = 0.1f; bool running = true; while (running) { app.handleEvents(running); + // Start timing + auto start = std::chrono::high_resolution_clock::now(); + particleSystem.update(deltaTime, emitters); // update particle system with delta time and emitters + // End timing + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration duration = end - start; // get duration in milliseconds + + std::cout << "Update took " << duration.count() << " ms" << std::endl; app.clearScreen(); // render particles using the drawSquare method from SDLApp -- cgit v1.2.3 From 5f84969c851530ebc430be2cf8e99c945ff7a4a7 Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Mon, 7 Oct 2024 18:07:17 +0200 Subject: improved particle system from 90ms to ~ 0.7ms --- src/crepe/Particle.cpp | 19 +++++++++++++------ src/crepe/Particle.hpp | 12 +++++++----- src/crepe/ParticleEmitter.cpp | 17 +++++++++++++++-- src/crepe/ParticleEmitter.hpp | 8 ++++---- src/crepe/ParticleSystem.cpp | 42 ++++++++++++++++++++++++++++++------------ src/crepe/SDLApp.cpp | 9 +++++++++ src/crepe/SDLApp.hpp | 5 ++++- src/crepe/main.cpp | 15 ++++++++++----- 8 files changed, 92 insertions(+), 35 deletions(-) diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp index 3254b5b..8cf7f7e 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -1,16 +1,23 @@ #include "Particle.hpp" #include -Particle::Particle(float lifespan, Position position, Position velocity) - : lifespan(lifespan), position(position), velocity(velocity), timeInLife(0.0f) {} +Particle::Particle() +{ + this->active = false; +} + +void Particle::reset(float lifespan, Position position, Position velocity) { + this->timeInLife = 0; + this->lifespan = lifespan; + this->position = position; + this->velocity = velocity; + this->active = true; +} void Particle::update(float deltaTime) { timeInLife += deltaTime; position.x += velocity.x * deltaTime; position.y += velocity.y * deltaTime; + if(timeInLife >= lifespan)this->active = false; } -bool Particle::isAlive() const { - std::cout << "lifespan" << lifespan << std::endl; - return timeInLife < lifespan; -} diff --git a/src/crepe/Particle.hpp b/src/crepe/Particle.hpp index f71fd67..669a8ab 100644 --- a/src/crepe/Particle.hpp +++ b/src/crepe/Particle.hpp @@ -1,18 +1,20 @@ #pragma once -class Particle { -public: - struct Position { +struct Position { float x; float y; }; +class Particle { +public: + Position position; Position velocity; float lifespan; + bool active; - Particle(float lifespan, Position position, Position velocity); + Particle(); + void reset(float lifespan, Position position, Position velocity); void update(float deltaTime); - bool isAlive() const; float timeInLife; }; diff --git a/src/crepe/ParticleEmitter.cpp b/src/crepe/ParticleEmitter.cpp index f5c18db..3c9cc4e 100644 --- a/src/crepe/ParticleEmitter.cpp +++ b/src/crepe/ParticleEmitter.cpp @@ -1,11 +1,24 @@ #include "ParticleEmitter.hpp" #include +#include "Particle.hpp" +#include ParticleEmitter::ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset, float m_beginLifespan, float m_endLifespan) : m_maxParticles(maxParticles), m_emissionRate(emissionRate), m_speed(speed), m_speedOffset(speedOffset), m_position{0, 0}, m_beginLifespan(m_beginLifespan),m_endLifespan(m_endLifespan) { std::srand(static_cast(std::time(nullptr))); // initialize random seed - + std::cout << "Create emitter" << std::endl; m_minAngle = (360 + angle - (angleOffset % 360)) % 360; // calculate minAngle m_maxAngle = (360 + angle + (angleOffset % 360)) % 360; // calculate maxAngle + for (size_t i = 0; i < m_maxParticles; i++) + { + this->particles.emplace_back(); + } + +} -} \ No newline at end of file +ParticleEmitter::~ParticleEmitter() { + std::vector::iterator it = this->particles.begin(); + while (it != this->particles.end()) { + it = this->particles.erase(it); + } +} diff --git a/src/crepe/ParticleEmitter.hpp b/src/crepe/ParticleEmitter.hpp index f647c27..5de5e1e 100644 --- a/src/crepe/ParticleEmitter.hpp +++ b/src/crepe/ParticleEmitter.hpp @@ -3,13 +3,12 @@ #include #include "Particle.hpp" + + class ParticleEmitter { public: ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset,float m_beginLifespan,float m_endLifespan); - struct Position { //struct to hold position - float x; - float y; - }; + ~ParticleEmitter(); Position m_position; //position of the emitter unsigned int m_maxParticles; //maximum number of particles @@ -22,4 +21,5 @@ public: float m_endLifespan; //begin Lifespan of particle std::vector particles; //collection of particles + }; diff --git a/src/crepe/ParticleSystem.cpp b/src/crepe/ParticleSystem.cpp index f3cb939..aff7a30 100644 --- a/src/crepe/ParticleSystem.cpp +++ b/src/crepe/ParticleSystem.cpp @@ -9,30 +9,32 @@ ParticleSystem::ParticleSystem() : m_elapsedTime(0.0f) {} // Initialize m_elapsedTime to 0 void ParticleSystem::update(float deltaTime, std::vector& emitters) { + // std::cout << "ParticleSystem update" << std::endl; for (ParticleEmitter& emitter : emitters) { float updateAmount = 1/static_cast(emitter.m_emissionRate); for (float i = 0; i < deltaTime; i += updateAmount) { emitParticle(emitter); } + // std::cout << "after emit" << std::endl; //update/move particles afterwards delete if not alive. - std::vector::iterator it = emitter.particles.begin(); - while (it != emitter.particles.end()) { - it->update(deltaTime); - if (!it->isAlive()) { - it = emitter.particles.erase(it); - } else { - ++it; + for (size_t j = 0; j < emitter.particles.size(); j++) + { + // std::cout << "update" << std::endl; + if(emitter.particles[j].active) + { + emitter.particles[j].update(deltaTime); } - } + } + } } void ParticleSystem::emitParticle(ParticleEmitter& emitter) { // std::cout << "new emitter:" << std::endl; - Particle::Position initialPosition = { emitter.m_position.x, emitter.m_position.y }; + Position initialPosition = { emitter.m_position.x, emitter.m_position.y }; float randomAngle = 0.0f; //check if value is overthe 360 degrees if(emitter.m_maxAngle < emitter.m_minAngle) @@ -49,11 +51,27 @@ void ParticleSystem::emitParticle(ParticleEmitter& emitter) { float randomSpeedOffset = (static_cast(std::rand()) / RAND_MAX) * (2 * emitter.m_speedOffset) - emitter.m_speedOffset; float velocityX = (emitter.m_speed + randomSpeedOffset) * std::cos(angleInRadians); float velocityY = (emitter.m_speed + randomSpeedOffset) * std::sin(angleInRadians); - Particle::Position initialVelocity = { + Position initialVelocity = { velocityX, velocityY }; // std::cout << "emitter.m_endLifespan:" << emitter.m_endLifespan << std::endl; - - emitter.particles.emplace_back(emitter.m_endLifespan, initialPosition, initialVelocity); + for (size_t i = 0; i < emitter.particles.size(); i++) + { + if(!emitter.particles[i].active) + { + // std::cout << "active " << emitter.particles[i].active << std::endl; + // std::cout << "lifespan " << emitter.particles[i].lifespan << std::endl; + // std::cout << "timeInLife " << emitter.particles[i].timeInLife << std::endl; + // std::cout << "emitter.m_endLifespan" << emitter.m_endLifespan << std::endl; + // std::cout << "initialPositionx" << initialPosition.x << std::endl; + // std::cout << "initialPositiony" << initialPosition.y << std::endl; + // std::cout << "initialVelocityx" << initialVelocity.x << std::endl; + // std::cout << "initialVelocityy" << initialVelocity.y << std::endl; + emitter.particles[i].reset(emitter.m_endLifespan, initialPosition, initialVelocity); + break; + } + } + + //emitter.particles.emplace_back(emitter.m_endLifespan, initialPosition, initialVelocity); } diff --git a/src/crepe/SDLApp.cpp b/src/crepe/SDLApp.cpp index a5e3621..0779af1 100644 --- a/src/crepe/SDLApp.cpp +++ b/src/crepe/SDLApp.cpp @@ -1,5 +1,8 @@ #include "SDLApp.hpp" #include +#include +#include "Particle.hpp" +#include "ParticleEmitter.hpp" SDLApp::SDLApp(int windowWidth, int windowHeight) : windowWidth(windowWidth), windowHeight(windowHeight), window(nullptr), renderer(nullptr) {} @@ -58,6 +61,12 @@ void SDLApp::drawSquare(int x, int y, int size) { SDL_RenderFillRect(renderer, &rect); } +SDL_Texture* squareTexture = nullptr; // Load this with an image or create it + + + + + void SDLApp::cleanUp() { if (renderer) { SDL_DestroyRenderer(renderer); diff --git a/src/crepe/SDLApp.hpp b/src/crepe/SDLApp.hpp index 92a4e27..f95d4bc 100644 --- a/src/crepe/SDLApp.hpp +++ b/src/crepe/SDLApp.hpp @@ -2,6 +2,8 @@ #define SDLAPP_HPP #include +#include "Particle.hpp" +#include "ParticleEmitter.hpp" class SDLApp { public: @@ -14,7 +16,8 @@ public: void presentScreen(); void drawSquare(int x, int y, int size); void cleanUp(); - + void drawParticles(const std::vector& emitters); + void drawMultipleSquares(const std::vector& squares); private: int windowWidth; int windowHeight; diff --git a/src/crepe/main.cpp b/src/crepe/main.cpp index 314dbed..58480a9 100644 --- a/src/crepe/main.cpp +++ b/src/crepe/main.cpp @@ -20,7 +20,7 @@ int main(int argc, char* argv[]) { ParticleSystem particleSystem; - unsigned int maxParticles = 10; // maximum number of particles + unsigned int maxParticles = 100; // maximum number of particles unsigned int emissionRate = 1; // particles created per second unsigned int speed = 50; // base speed of particles unsigned int speedOffset = 10; // random offset for particle speed @@ -33,7 +33,7 @@ int main(int argc, char* argv[]) { std::vector emitters; // Loop to create 1000 emitters - for (unsigned int i = 0; i < 100; ++i) { + for (unsigned int i = 0; i < 1000; ++i) { ParticleEmitter emitter(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset, beginLifespan, endLifespan); // Set a position for each emitter, modifying the position for demonstration @@ -43,7 +43,7 @@ int main(int argc, char* argv[]) { } float deltaTime = 0.1f; bool running = true; - + std::cout << "start loop " << std::endl; while (running) { app.handleEvents(running); @@ -59,15 +59,20 @@ int main(int argc, char* argv[]) { std::cout << "Update took " << duration.count() << " ms" << std::endl; app.clearScreen(); + start = std::chrono::high_resolution_clock::now(); // render particles using the drawSquare method from SDLApp for (const ParticleEmitter& emitter : emitters) { for (const Particle& particle : emitter.particles) { - - app.drawSquare(particle.position.x, particle.position.y, 5); // draw each particle + if(particle.active)app.drawSquare(particle.position.x, particle.position.y, 5); // draw each particle } } + app.presentScreen(); + end = std::chrono::high_resolution_clock::now(); + duration = end - start; // get duration in milliseconds + + std::cout << "screen took " << duration.count() << " ms" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(20)); // simulate ~50 FPS } -- cgit v1.2.3 From afdd12277a43d3ad7755f028e85c569dece84f0b Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Tue, 8 Oct 2024 15:43:45 +0200 Subject: rendering system --- src/CMakeLists.txt | 2 +- src/crepe/CMakeLists.txt | 1 + src/crepe/api/CMakeLists.txt | 5 ++ src/crepe/api/Color.cpp | 53 +++++++++++++++ src/crepe/api/Color.h | 34 ++++++++++ src/crepe/api/Point.h | 14 ++++ src/crepe/api/Sprite.h | 28 ++++++++ src/crepe/api/Transform.h | 13 ++++ src/crepe/api/game.cpp | 26 +++++-- src/crepe/api/game.h | 16 +++-- src/crepe/api/spritesheet.cpp | 6 -- src/crepe/api/spritesheet.h | 4 -- src/crepe/core/CMakeLists.txt | 8 +++ src/crepe/core/renderSystem.cpp | 37 ++++++++++ src/crepe/core/renderSystem.h | 13 ++++ src/crepe/facade/SdlContext.cpp | 145 +++++++++++++++++++++------------------- src/crepe/facade/SdlContext.h | 19 +++--- src/crepe/facade/Texture.cpp | 6 +- src/crepe/facade/Texture.h | 8 ++- src/dummy_rendering.cpp | 16 +++++ 20 files changed, 349 insertions(+), 105 deletions(-) create mode 100644 src/crepe/api/Color.cpp create mode 100644 src/crepe/api/Color.h create mode 100644 src/crepe/api/Point.h create mode 100644 src/crepe/api/Sprite.h create mode 100644 src/crepe/api/Transform.h create mode 100644 src/crepe/core/CMakeLists.txt create mode 100644 src/crepe/core/renderSystem.cpp create mode 100644 src/crepe/core/renderSystem.h create mode 100644 src/dummy_rendering.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d29a54..a9193fd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,6 @@ install( FILE_SET HEADERS DESTINATION include/crepe ) -add_executable(dummy_rm dummy_resource_manager.cpp) +add_executable(dummy_rm dummy_rendering.cpp) #add_executable(dummy_rm dummy_audio.cpp) target_link_libraries(dummy_rm PUBLIC crepe) diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 69e67ac..05f14d1 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -8,4 +8,5 @@ add_subdirectory(api) add_subdirectory(util) add_subdirectory(fabricator) add_subdirectory(facade) +add_subdirectory(core) diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 96b55cf..dcac0ae 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -4,6 +4,7 @@ target_sources(crepe PUBLIC resource_manager.cpp Resource.cpp game.cpp + Color.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -15,4 +16,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES resource_manager.h Component.h AudioSource.h + Sprite.h + Color.h + Transform.h + Point.h ) diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp new file mode 100644 index 0000000..c73ce6c --- /dev/null +++ b/src/crepe/api/Color.cpp @@ -0,0 +1,53 @@ + + +#include "Color.h" + + +using namespace crepe::api; + +Color Color::white = Color(255,255,255,0); +Color Color::red = Color(255,0,0,0); +Color Color::green = Color(0,255,0,0); +Color Color::blue = Color(0,0,255,0); +Color Color::black = Color(0,0,0,0); +Color Color::cyan = Color(0,255,255,0); +Color Color::yellow = Color(255,255,0,0); +Color Color::magenta= Color(255,0,255,0); + +Color::Color(double red, double green, double blue, double alpha){ + this->a = alpha; + this->r = red; + this->g = green; + this->b = blue; +}; + +const Color& Color::get_white(){ + return Color::white; +}; + +const Color& Color::get_red(){ + return Color::red; +}; +const Color& Color::get_green(){ + return Color::green; +}; +const Color& Color::get_blue(){ + return Color::blue; +}; + +const Color& Color::get_black(){ + return Color::black; +}; + +const Color& Color::get_cyan(){ + return Color::cyan; +}; + +const Color& Color::get_yellow(){ + return Color::yellow; +}; + +const Color& Color::get_magenta(){ + return Color::magenta; +}; + diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h new file mode 100644 index 0000000..207434e --- /dev/null +++ b/src/crepe/api/Color.h @@ -0,0 +1,34 @@ +#pragma once + +namespace crepe::api { + +class Color { + +public: + Color(double red, double green, double blue, double alpha); + static const Color & get_white(); + static const Color & get_red(); + static const Color & get_green(); + static const Color & get_blue(); + static const Color & get_cyan(); + static const Color & get_magenta(); + static const Color & get_yellow(); + static const Color & get_black(); + +private: + double r; + double g; + double b; + double a; + + static Color white; + static Color red; + static Color green; + static Color blue; + static Color cyan; + static Color magenta; + static Color yellow; + static Color black; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Point.h b/src/crepe/api/Point.h new file mode 100644 index 0000000..463aa7c --- /dev/null +++ b/src/crepe/api/Point.h @@ -0,0 +1,14 @@ +#pragma once + + + +namespace crepe::api { + +class Point { +public: + double x; + double y; +}; + + +} diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h new file mode 100644 index 0000000..84eeb83 --- /dev/null +++ b/src/crepe/api/Sprite.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Component.h" +#include "api/Color.h" +#include "facade/Texture.h" +#include + + +namespace crepe::api { + +struct flip_settings{ + bool flipX : 1; + bool flipY : 1; +}; +class Sprite : public Component { + +public: + Sprite(crepe::Texture& image, const Color& color, const flip_settings& flip ) : sprite_image(&image), color(color), flip(flip){} + crepe::Texture* sprite_image; + Color color; + flip_settings flip; + uint8_t sortingLayer; + uint8_t orderInLayer; + + +}; + +} diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h new file mode 100644 index 0000000..d4dfafc --- /dev/null +++ b/src/crepe/api/Transform.h @@ -0,0 +1,13 @@ +#pragma once + +#include "api/Component.h" +#include "api/Point.h" +namespace crepe::api { + +class Transform : public Component { +public: + Point position; // Translation (shift) + double rotation; // Rotation, in radians + double scale; // Multiplication factoh +}; +} // namespace crepe::api diff --git a/src/crepe/api/game.cpp b/src/crepe/api/game.cpp index 02a0132..01920a9 100644 --- a/src/crepe/api/game.cpp +++ b/src/crepe/api/game.cpp @@ -2,17 +2,29 @@ #include "game.h" -#include "api/spritesheet.h" +#include "core/renderSystem.h" #include "facade/SdlContext.h" -#include "facade/Texture.h" -#include -void game::render(std::vector & draw, std::vector & ss){ - auto& ctx = crepe::SdlContext::get_instance(); - - ctx.loop(*draw[0], *ss[0]); +Engine::Engine(int windowHeight, int window_with){ + crepe::SdlContext& ctx = crepe::SdlContext::get_instance(); } +void Engine::loop() { + + bool running = true; + crepe::SdlContext& ctx = crepe::SdlContext::get_instance(); + RenderSystem rendering; + + while (running) { + ctx.handleEvents(running); + + ctx.clearScreen(); + + rendering.render(); + + ctx.presentScreen(); + } +} diff --git a/src/crepe/api/game.h b/src/crepe/api/game.h index 7cde954..64027fa 100644 --- a/src/crepe/api/game.h +++ b/src/crepe/api/game.h @@ -1,15 +1,17 @@ #pragma once -#include "api/spritesheet.h" -#include "facade/Texture.h" -#include -class game { +class Engine{ public: - game(){} - ~game(){} + Engine(int windowWith, int windowHeight); + ~Engine() = default; - void render(std::vector&, std::vector&); + void loop(); + + +private: + int window_height; + int window_width; }; diff --git a/src/crepe/api/spritesheet.cpp b/src/crepe/api/spritesheet.cpp index 93a2b65..7f5da38 100644 --- a/src/crepe/api/spritesheet.cpp +++ b/src/crepe/api/spritesheet.cpp @@ -30,12 +30,6 @@ void Spritesheet::select_sprite(const int x, const int y){ m_clip.y = y * m_clip.h; } -void Spritesheet::draw_selected_sprite(const int x, const int y){ - auto& ctx = SdlContext::get_instance(); - SDL_Rect tmp = { x, y, m_clip.w, m_clip.h}; - SDL_RenderCopy(ctx.m_game_renderer, this->m_spritesheet, &this->m_clip, &tmp); -} - void Spritesheet::load(std::unique_ptr res, const int row, const int col){ auto& ctx = SdlContext::get_instance(); diff --git a/src/crepe/api/spritesheet.h b/src/crepe/api/spritesheet.h index 7f46296..503dcef 100644 --- a/src/crepe/api/spritesheet.h +++ b/src/crepe/api/spritesheet.h @@ -24,14 +24,10 @@ private: void load(std::unique_ptr res, const int row, const int col);; SDL_Texture* get_texture() const; - - private: SDL_Texture* m_spritesheet; SDL_Rect m_clip; - - friend class SdlContext; }; } diff --git a/src/crepe/core/CMakeLists.txt b/src/crepe/core/CMakeLists.txt new file mode 100644 index 0000000..c44f0f6 --- /dev/null +++ b/src/crepe/core/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(crepe PUBLIC + renderSystem.cpp +) + +target_sources(crepe PUBLIC FILE_SET HEADERS FILES + renderSystem.h +) + diff --git a/src/crepe/core/renderSystem.cpp b/src/crepe/core/renderSystem.cpp new file mode 100644 index 0000000..a06aeba --- /dev/null +++ b/src/crepe/core/renderSystem.cpp @@ -0,0 +1,37 @@ + + + +#include "renderSystem.h" +#include + +#include "api/Color.h" +#include "api/Sprite.h" +#include "api/Transform.h" +#include "facade/SdlContext.h" +#include "facade/Texture.h" + +using namespace crepe::api; + + +static crepe::Texture player("../asset/texture/img.png"); + + +void RenderSystem::render(){ + + Sprite sprite(player, Color::get_red(), {1,1}); + Transform transform ={ + .position = {0,0}, + .rotation = 0, + .scale = 1, + }; + + // this will get changed to ecs getter of componets + crepe::SdlContext& ctx = crepe::SdlContext::get_instance(); + + ctx.draw(sprite, transform); + /* + for(const auto& S : test_objects){ + ctx.draw(S, const api::Transform &) + } + */ +} diff --git a/src/crepe/core/renderSystem.h b/src/crepe/core/renderSystem.h new file mode 100644 index 0000000..9011b30 --- /dev/null +++ b/src/crepe/core/renderSystem.h @@ -0,0 +1,13 @@ + +#pragma once + + + +class RenderSystem { + +public: + RenderSystem() = default; + ~RenderSystem() = default; + + void render(); +}; diff --git a/src/crepe/facade/SdlContext.cpp b/src/crepe/facade/SdlContext.cpp index 7e2d79f..b2043e5 100644 --- a/src/crepe/facade/SdlContext.cpp +++ b/src/crepe/facade/SdlContext.cpp @@ -2,54 +2,88 @@ #include "SdlContext.h" #include "SDL_rect.h" -#include "api/spritesheet.h" +#include "api/Sprite.h" +#include "api/Transform.h" #include "facade/Texture.h" #include "util/log.h" #include +#include #include #include #include -#include +#include #include -#include #include using namespace crepe; - -SdlContext& SdlContext::get_instance(){ +SdlContext & SdlContext::get_instance() { static SdlContext instance; return instance; } +void SdlContext::handleEvents(bool & running) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + running = false; + } + } +} +void SdlContext::clearScreen() { SDL_RenderClear(this->m_game_renderer); } + +void SdlContext::presentScreen() { SDL_RenderPresent(this->m_game_renderer); } -SdlContext::SdlContext(){ - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; - return; - } +void SdlContext::draw(const api::Sprite & sprite, const api::Transform& transform) { + static SDL_RendererFlip renderFlip + = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flipX) + | (SDL_FLIP_VERTICAL * sprite.flip.flipY)); - m_game_window = SDL_CreateWindow("Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN); - if (!m_game_window) { - std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl; - } + // needs maybe camera for position + static SDL_Rect dstrect = { + .x = static_cast(transform.position.x), + .y = static_cast(transform.position.y), + .w = static_cast(sprite.sprite_image->get_rect().w * transform.scale), + .h = static_cast(sprite.sprite_image->get_rect().h * transform.scale), + }; - m_game_renderer = SDL_CreateRenderer(m_game_window, -1, SDL_RENDERER_ACCELERATED); - if (!m_game_renderer) { - std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() << std::endl; + SDL_RenderCopyEx(this->m_game_renderer, sprite.sprite_image->get_texture(), + &sprite.sprite_image->get_rect(), &dstrect, 0, NULL, renderFlip); +} + +SdlContext::SdlContext() { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() + << std::endl; + return; + } + + m_game_window + = SDL_CreateWindow("Crepe Game Engine", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, 1920, 1080, SDL_WINDOW_SHOWN); + if (!m_game_window) { + std::cerr << "Window could not be created! SDL_Error: " + << SDL_GetError() << std::endl; + } + + m_game_renderer + = SDL_CreateRenderer(m_game_window, -1, SDL_RENDERER_ACCELERATED); + if (!m_game_renderer) { + std::cerr << "Renderer could not be created! SDL_Error: " + << SDL_GetError() << std::endl; SDL_DestroyWindow(m_game_window); - return; - } - int imgFlags = IMG_INIT_PNG; - if( !( IMG_Init( imgFlags ) & imgFlags ) ) - { - std::cout << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError() << std::endl; - } + return; + } + int imgFlags = IMG_INIT_PNG; + if (!(IMG_Init(imgFlags) & imgFlags)) { + std::cout << "SDL_image could not initialize! SDL_image Error: " + << IMG_GetError() << std::endl; + } + SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1"); } -SdlContext::~SdlContext(){ - if(m_game_renderer) - SDL_DestroyRenderer(m_game_renderer); +SdlContext::~SdlContext() { + if (m_game_renderer) SDL_DestroyRenderer(m_game_renderer); if (m_game_window) { SDL_DestroyWindow(m_game_window); @@ -58,69 +92,46 @@ SdlContext::~SdlContext(){ SDL_Quit(); } -SDL_Texture* SdlContext::setTextureFromPath(const char* path, SDL_Rect& clip, const int row, const int col){ +SDL_Texture * SdlContext::setTextureFromPath(const char * path, SDL_Rect & clip, + const int row, const int col) { dbg_trace(); - - SDL_Surface* tmp = IMG_Load(path); + + SDL_Surface * tmp = IMG_Load(path); if (!tmp) { std::cerr << "Error surface " << IMG_GetError << std::endl; } - clip.w = tmp->w / col; - clip.h = tmp->h / row; + clip.w = tmp->w / col; + clip.h = tmp->h / row; - SDL_Texture* CreatedTexture = SDL_CreateTextureFromSurface(m_game_renderer, tmp); + SDL_Texture * CreatedTexture + = SDL_CreateTextureFromSurface(m_game_renderer, tmp); if (!CreatedTexture) { - std::cerr << "Error could not create texture " << IMG_GetError << std::endl; + std::cerr << "Error could not create texture " << IMG_GetError + << std::endl; } SDL_FreeSurface(tmp); return CreatedTexture; } -SDL_Texture* SdlContext::setTextureFromPath(const char* path){ +SDL_Texture * SdlContext::setTextureFromPath(const char * path) { dbg_trace(); - - SDL_Surface* tmp = IMG_Load(path); + + SDL_Surface * tmp = IMG_Load(path); if (!tmp) { std::cerr << "Error surface " << IMG_GetError << std::endl; } - SDL_Texture* CreatedTexture = SDL_CreateTextureFromSurface(m_game_renderer, tmp); + SDL_Texture * CreatedTexture + = SDL_CreateTextureFromSurface(m_game_renderer, tmp); if (!CreatedTexture) { - std::cerr << "Error could not create texture " << IMG_GetError << std::endl; + std::cerr << "Error could not create texture " << IMG_GetError + << std::endl; } SDL_FreeSurface(tmp); return CreatedTexture; } - -void SdlContext::loop(const Texture& texture, api::Spritesheet& ss){ - SDL_RenderClear(m_game_renderer); - bool quit = false; - SDL_Event event; - - while (!quit) { - Uint32 ticks = SDL_GetTicks(); - int sprite = (ticks / 100) % 4; - ss.select_sprite(sprite, 0); - - while (SDL_PollEvent(&event) != NULL) { - switch (event.type) { - case SDL_QUIT: - quit = true; - break; - } - - } - - SDL_RenderClear(m_game_renderer); - SDL_RenderCopy(m_game_renderer, texture.get_texture(), NULL, NULL); - ss.draw_selected_sprite(10, 10); - SDL_RenderPresent(m_game_renderer); - } -} - - diff --git a/src/crepe/facade/SdlContext.h b/src/crepe/facade/SdlContext.h index 329a374..c8f1304 100644 --- a/src/crepe/facade/SdlContext.h +++ b/src/crepe/facade/SdlContext.h @@ -1,23 +1,28 @@ #pragma once #include "SDL_rect.h" -#include "Texture.h" -#include "api/spritesheet.h" +#include "api/Sprite.h" +#include "api/Transform.h" #include #include namespace crepe { -class Texture; -class Spritesheet; class SdlContext { public: - void loop(const Texture& , api::Spritesheet&); + + void handleEvents(bool& running); + void clearScreen(); + void presentScreen(); + void draw(const api::Sprite&, const api::Transform&); // singleton static SdlContext & get_instance(); + SDL_Texture* setTextureFromPath(const char*); + SDL_Texture* setTextureFromPath(const char*, SDL_Rect& clip, const int row, const int col); + private: SdlContext(); virtual ~SdlContext(); @@ -27,12 +32,8 @@ private: SdlContext & operator=(const SdlContext &) = delete; SdlContext & operator=(SdlContext &&) = delete; - SDL_Texture* setTextureFromPath(const char*); - SDL_Texture* setTextureFromPath(const char*, SDL_Rect& clip, const int row, const int col); private: - friend class Texture; - friend class api::Spritesheet; SDL_Window* m_game_window; SDL_Renderer* m_game_renderer; diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp index 220ef2e..b4e3aa8 100644 --- a/src/crepe/facade/Texture.cpp +++ b/src/crepe/facade/Texture.cpp @@ -27,9 +27,13 @@ Texture::~Texture(){ void Texture::load(std::unique_ptr res) { dbg_trace(); SdlContext& ctx = SdlContext::get_instance(); - m_texture = ctx.setTextureFromPath(res->canonical()); + m_texture = ctx.setTextureFromPath(res->canonical(), srcrect, 1, 1); } SDL_Texture* Texture::get_texture() const{ return m_texture; } + +SDL_Rect& Texture::get_rect() { + return srcrect; +} diff --git a/src/crepe/facade/Texture.h b/src/crepe/facade/Texture.h index a5fcca9..db2f1f9 100644 --- a/src/crepe/facade/Texture.h +++ b/src/crepe/facade/Texture.h @@ -1,13 +1,15 @@ #pragma once +#include "SDL_rect.h" #include "api/baseResource.h" -#include "facade/SdlContext.h" #include "api/Resource.h" #include #include namespace crepe { + + class Texture : public api::BaseResource{ public: @@ -16,13 +18,13 @@ public: ~Texture(); SDL_Texture* get_texture() const; + SDL_Rect& get_rect() ; private: void load(std::unique_ptr res); private: SDL_Texture* m_texture; - - friend class SdlContext; + SDL_Rect srcrect; }; } // namespace crepe diff --git a/src/dummy_rendering.cpp b/src/dummy_rendering.cpp new file mode 100644 index 0000000..9bbf92d --- /dev/null +++ b/src/dummy_rendering.cpp @@ -0,0 +1,16 @@ + + + + + + + + +#include "api/game.h" +int main(){ + + Engine engine(800,600); + + engine.loop(); + +} -- cgit v1.2.3 From 97621ae53dfc3434cd97e45266bd4f4ac2ef49e1 Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Wed, 9 Oct 2024 20:01:45 +0200 Subject: added sdl2 include --- src/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f11402a..e63c1f9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ add_compile_definitions(DEBUG) add_subdirectory(../lib/soloud soloud) add_subdirectory(../lib/googletest googletest) +add_subdirectory(../lib/sdl2 sdl2) project(crepe C CXX) @@ -22,6 +23,7 @@ target_include_directories(crepe # TODO: libraries should be linked as PRIVATE target_link_libraries(crepe PUBLIC soloud + PUBLIC sdl2 ) add_subdirectory(crepe) @@ -38,4 +40,4 @@ target_link_libraries(test_main PUBLIC crepe ) -add_subdirectory(crepe) \ No newline at end of file +add_subdirectory(crepe) -- cgit v1.2.3 From f1e830392b084be776286a61409d0e4a2755c2cf Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Thu, 10 Oct 2024 07:33:31 +0200 Subject: quick fix --- src/crepe/util/log.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crepe/util/log.h b/src/crepe/util/log.h index 2b0fbe1..763b314 100644 --- a/src/crepe/util/log.h +++ b/src/crepe/util/log.h @@ -9,7 +9,7 @@ #define _crepe_logf_here(fmt, ...) \ crepe::util::logf(util::log_level::DEBUG, "%s%s (%s:%d)" fmt "\n", \ crepe::util::color::FG_WHITE, __PRETTY_FUNCTION__, \ - __FILE_NAME__, __LINE__, crepe::util::color::RESET, \ + __FILE__, __LINE__, crepe::util::color::RESET, \ __VA_ARGS__) // very illegal global function-style macros -- cgit v1.2.3 From 163c9e3eea437daa8ef6007fbdf2f91470066cbf Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Thu, 10 Oct 2024 07:49:38 +0200 Subject: fix for build --- src/CMakeLists.txt | 2 -- src/crepe/CMakeLists.txt | 1 - src/example/CMakeLists.txt | 6 ++---- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f6afdc0..446433c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,5 +36,3 @@ target_link_libraries(test_main PRIVATE gtest_main PUBLIC crepe ) - -add_subdirectory(crepe) diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index a02e991..9c65e1e 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -3,7 +3,6 @@ target_sources(crepe PUBLIC Asset.cpp Sound.cpp SoundContext.cpp - main.cpp Particle.cpp ParticleEmitter.cpp ParticleSystem.cpp diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index f66e08a..36ceba1 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -15,7 +15,5 @@ endfunction() add_example(audio_internal) add_example(components_internal) add_example(script) - -add_executable(particel EXCLUDE_FROM_ALL particel.cpp) -target_link_libraries(particel PUBLIC crepe) - +add_example(particel) +target_link_libraries(particel PUBLIC SDL2) -- cgit v1.2.3 From d001c4ba95a72f13c942f1a24eb98fe1584d93a4 Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Thu, 10 Oct 2024 08:38:40 +0200 Subject: Renamed files and Emitter is a component --- src/crepe/CMakeLists.txt | 8 ++++---- src/crepe/Particle.cpp | 4 +++- src/crepe/Particle.h | 24 ++++++++++++++++++++++++ src/crepe/Particle.hpp | 20 -------------------- src/crepe/ParticleEmitter.cpp | 10 ++++++---- src/crepe/ParticleEmitter.h | 32 ++++++++++++++++++++++++++++++++ src/crepe/ParticleEmitter.hpp | 25 ------------------------- src/crepe/ParticleSystem.cpp | 12 +++++++----- src/crepe/ParticleSystem.h | 19 +++++++++++++++++++ src/crepe/ParticleSystem.hpp | 14 -------------- src/crepe/SDLApp.cpp | 6 +++--- src/crepe/SDLApp.h | 28 ++++++++++++++++++++++++++++ src/crepe/SDLApp.hpp | 28 ---------------------------- src/example/particel.cpp | 10 ++++++---- 14 files changed, 132 insertions(+), 108 deletions(-) create mode 100644 src/crepe/Particle.h delete mode 100644 src/crepe/Particle.hpp create mode 100644 src/crepe/ParticleEmitter.h delete mode 100644 src/crepe/ParticleEmitter.hpp create mode 100644 src/crepe/ParticleSystem.h delete mode 100644 src/crepe/ParticleSystem.hpp create mode 100644 src/crepe/SDLApp.h delete mode 100644 src/crepe/SDLApp.hpp diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 9c65e1e..399200b 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -21,10 +21,10 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Asset.h Sound.h SoundContext.h - ParticleEmitter.hpp - ParticleSystem.hpp - Particle.hpp - SDLApp.hpp + ParticleEmitter.h + ParticleSystem.h + Particle.h + SDLApp.h ComponentManager.h ComponentManager.hpp Component.h diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp index 8cf7f7e..aa33606 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -1,6 +1,8 @@ -#include "Particle.hpp" +#include "Particle.h" #include +using namespace crepe; + Particle::Particle() { this->active = false; diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h new file mode 100644 index 0000000..c408ed9 --- /dev/null +++ b/src/crepe/Particle.h @@ -0,0 +1,24 @@ +#pragma once + +namespace crepe { + +struct Position { + float x; + float y; + }; + +class Particle { +public: + + Position position; + Position velocity; + float lifespan; + bool active; + + Particle(); + void reset(float lifespan, Position position, Position velocity); + void update(float deltaTime); + float timeInLife; +}; + +} diff --git a/src/crepe/Particle.hpp b/src/crepe/Particle.hpp deleted file mode 100644 index 669a8ab..0000000 --- a/src/crepe/Particle.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -struct Position { - float x; - float y; - }; - -class Particle { -public: - - Position position; - Position velocity; - float lifespan; - bool active; - - Particle(); - void reset(float lifespan, Position position, Position velocity); - void update(float deltaTime); - float timeInLife; -}; diff --git a/src/crepe/ParticleEmitter.cpp b/src/crepe/ParticleEmitter.cpp index 3c9cc4e..9ae5e76 100644 --- a/src/crepe/ParticleEmitter.cpp +++ b/src/crepe/ParticleEmitter.cpp @@ -1,11 +1,13 @@ -#include "ParticleEmitter.hpp" +#include "ParticleEmitter.h" #include -#include "Particle.hpp" +#include "Particle.h" #include -ParticleEmitter::ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset, float m_beginLifespan, float m_endLifespan) +using namespace crepe; + +ParticleEmitter::ParticleEmitter(uint32_t maxParticles, uint32_t emissionRate, uint32_t speed, uint32_t speedOffset, uint32_t angle, uint32_t angleOffset, float m_beginLifespan, float m_endLifespan) : m_maxParticles(maxParticles), m_emissionRate(emissionRate), m_speed(speed), m_speedOffset(speedOffset), m_position{0, 0}, m_beginLifespan(m_beginLifespan),m_endLifespan(m_endLifespan) { - std::srand(static_cast(std::time(nullptr))); // initialize random seed + std::srand(static_cast(std::time(nullptr))); // initialize random seed std::cout << "Create emitter" << std::endl; m_minAngle = (360 + angle - (angleOffset % 360)) % 360; // calculate minAngle m_maxAngle = (360 + angle + (angleOffset % 360)) % 360; // calculate maxAngle diff --git a/src/crepe/ParticleEmitter.h b/src/crepe/ParticleEmitter.h new file mode 100644 index 0000000..477f492 --- /dev/null +++ b/src/crepe/ParticleEmitter.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "Particle.h" +#include +#include "Component.h" +# + +namespace crepe { + +class ParticleEmitter : public Component { +public: + ParticleEmitter(uint32_t maxParticles, uint32_t emissionRate, uint32_t speed, uint32_t speedOffset, uint32_t angle, uint32_t angleOffset,float m_beginLifespan,float m_endLifespan); + ~ParticleEmitter(); + + Position m_position; //position of the emitter + uint32_t m_maxParticles; //maximum number of particles + uint32_t m_emissionRate; //rate of particle emission + uint32_t m_speed; //base speed of the particles + uint32_t m_speedOffset; //offset for random speed variation + uint32_t m_minAngle; //min angle of particle emission + uint32_t m_maxAngle; //max angle of particle emission + float m_beginLifespan; //begin Lifespan of particle (only visual) + float m_endLifespan; //begin Lifespan of particle + + std::vector particles; //collection of particles + +}; + +} + + diff --git a/src/crepe/ParticleEmitter.hpp b/src/crepe/ParticleEmitter.hpp deleted file mode 100644 index 5de5e1e..0000000 --- a/src/crepe/ParticleEmitter.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include "Particle.hpp" - - - -class ParticleEmitter { -public: - ParticleEmitter(unsigned int maxParticles, unsigned int emissionRate, unsigned int speed, unsigned int speedOffset, unsigned int angle, unsigned int angleOffset,float m_beginLifespan,float m_endLifespan); - ~ParticleEmitter(); - - Position m_position; //position of the emitter - unsigned int m_maxParticles; //maximum number of particles - unsigned int m_emissionRate; //rate of particle emission - unsigned int m_speed; //base speed of the particles - unsigned int m_speedOffset; //offset for random speed variation - unsigned int m_minAngle; //min angle of particle emission - unsigned int m_maxAngle; //max angle of particle emission - float m_beginLifespan; //begin Lifespan of particle (only visual) - float m_endLifespan; //begin Lifespan of particle - - std::vector particles; //collection of particles - -}; diff --git a/src/crepe/ParticleSystem.cpp b/src/crepe/ParticleSystem.cpp index aff7a30..8891e2b 100644 --- a/src/crepe/ParticleSystem.cpp +++ b/src/crepe/ParticleSystem.cpp @@ -1,10 +1,12 @@ -#include "ParticleSystem.hpp" +#include "ParticleSystem.h" #include // #include #include #include // include iostream for std::cout -#include "ParticleEmitter.hpp" -#include "Particle.hpp" +#include "ParticleEmitter.h" +#include "Particle.h" + +using namespace crepe; ParticleSystem::ParticleSystem() : m_elapsedTime(0.0f) {} // Initialize m_elapsedTime to 0 @@ -39,11 +41,11 @@ void ParticleSystem::emitParticle(ParticleEmitter& emitter) { //check if value is overthe 360 degrees if(emitter.m_maxAngle < emitter.m_minAngle) { - randomAngle = ((emitter.m_minAngle + (std::rand() % (static_cast(emitter.m_maxAngle + 360 - emitter.m_minAngle + 1))))%360); + randomAngle = ((emitter.m_minAngle + (std::rand() % (static_cast(emitter.m_maxAngle + 360 - emitter.m_minAngle + 1))))%360); } else { - randomAngle = emitter.m_minAngle + (std::rand() % (static_cast(emitter.m_maxAngle - emitter.m_minAngle + 1))); + randomAngle = emitter.m_minAngle + (std::rand() % (static_cast(emitter.m_maxAngle - emitter.m_minAngle + 1))); } diff --git a/src/crepe/ParticleSystem.h b/src/crepe/ParticleSystem.h new file mode 100644 index 0000000..fd6d110 --- /dev/null +++ b/src/crepe/ParticleSystem.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "ParticleEmitter.h" + + +namespace crepe { + +class ParticleSystem { +public: + ParticleSystem(); + void update(float deltaTime, std::vector& emitters); +private: + void emitParticle(ParticleEmitter &emitter); //emits a new particle + + float m_elapsedTime; //elapsed time since the last emission +}; + +} diff --git a/src/crepe/ParticleSystem.hpp b/src/crepe/ParticleSystem.hpp deleted file mode 100644 index f670415..0000000 --- a/src/crepe/ParticleSystem.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include "ParticleEmitter.hpp" - -class ParticleSystem { -public: - ParticleSystem(); - void update(float deltaTime, std::vector& emitters); -private: - void emitParticle(ParticleEmitter &emitter); //emits a new particle - - float m_elapsedTime; //elapsed time since the last emission -}; diff --git a/src/crepe/SDLApp.cpp b/src/crepe/SDLApp.cpp index 0779af1..715dd6f 100644 --- a/src/crepe/SDLApp.cpp +++ b/src/crepe/SDLApp.cpp @@ -1,8 +1,8 @@ -#include "SDLApp.hpp" +#include "SDLApp.h" #include #include -#include "Particle.hpp" -#include "ParticleEmitter.hpp" +#include "Particle.h" +#include "ParticleEmitter.h" SDLApp::SDLApp(int windowWidth, int windowHeight) : windowWidth(windowWidth), windowHeight(windowHeight), window(nullptr), renderer(nullptr) {} diff --git a/src/crepe/SDLApp.h b/src/crepe/SDLApp.h new file mode 100644 index 0000000..8915d30 --- /dev/null +++ b/src/crepe/SDLApp.h @@ -0,0 +1,28 @@ +#ifndef SDLAPP_HPP +#define SDLAPP_HPP + +#include +#include "Particle.h" +#include "ParticleEmitter.h" + +class SDLApp { +public: + SDLApp(int windowWidth, int windowHeight); + ~SDLApp(); + + bool initialize(); + void handleEvents(bool& running); + void clearScreen(); + void presentScreen(); + void drawSquare(int x, int y, int size); + void cleanUp(); + void drawParticles(const std::vector& emitters); + void drawMultipleSquares(const std::vector& squares); +private: + int windowWidth; + int windowHeight; + SDL_Window* window; + SDL_Renderer* renderer; +}; + +#endif diff --git a/src/crepe/SDLApp.hpp b/src/crepe/SDLApp.hpp deleted file mode 100644 index f95d4bc..0000000 --- a/src/crepe/SDLApp.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SDLAPP_HPP -#define SDLAPP_HPP - -#include -#include "Particle.hpp" -#include "ParticleEmitter.hpp" - -class SDLApp { -public: - SDLApp(int windowWidth, int windowHeight); - ~SDLApp(); - - bool initialize(); - void handleEvents(bool& running); - void clearScreen(); - void presentScreen(); - void drawSquare(int x, int y, int size); - void cleanUp(); - void drawParticles(const std::vector& emitters); - void drawMultipleSquares(const std::vector& squares); -private: - int windowWidth; - int windowHeight; - SDL_Window* window; - SDL_Renderer* renderer; -}; - -#endif diff --git a/src/example/particel.cpp b/src/example/particel.cpp index 58480a9..c66bbef 100644 --- a/src/example/particel.cpp +++ b/src/example/particel.cpp @@ -1,12 +1,14 @@ #include #include #include -#include "SDLApp.hpp" -#include "ParticleEmitter.hpp" -#include "ParticleSystem.hpp" -#include "Particle.hpp" +#include "SDLApp.h" +#include "ParticleEmitter.h" +#include "ParticleSystem.h" +#include "Particle.h" #include +using namespace crepe; + const int WINDOW_WIDTH = 800; const int WINDOW_HEIGHT = 600; -- cgit v1.2.3 From 016070d47f2fff8cfdd49cc0bdf65c14ef922c94 Mon Sep 17 00:00:00 2001 From: jaroWMR Date: Thu, 10 Oct 2024 09:30:49 +0200 Subject: renamed particel to particle and example uses ecs for poc --- src/crepe/ParticleEmitter.cpp | 2 + src/crepe/ParticleSystem.cpp | 27 ++---------- src/crepe/ParticleSystem.h | 2 +- src/example/CMakeLists.txt | 4 +- src/example/particel.cpp | 84 ------------------------------------- src/example/particle.cpp | 97 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 105 insertions(+), 111 deletions(-) delete mode 100644 src/example/particel.cpp create mode 100644 src/example/particle.cpp diff --git a/src/crepe/ParticleEmitter.cpp b/src/crepe/ParticleEmitter.cpp index 9ae5e76..30cba7c 100644 --- a/src/crepe/ParticleEmitter.cpp +++ b/src/crepe/ParticleEmitter.cpp @@ -11,6 +11,8 @@ ParticleEmitter::ParticleEmitter(uint32_t maxParticles, uint32_t emissionRate, u std::cout << "Create emitter" << std::endl; m_minAngle = (360 + angle - (angleOffset % 360)) % 360; // calculate minAngle m_maxAngle = (360 + angle + (angleOffset % 360)) % 360; // calculate maxAngle + m_position.x = 400; + m_position.y = 400; for (size_t i = 0; i < m_maxParticles; i++) { this->particles.emplace_back(); diff --git a/src/crepe/ParticleSystem.cpp b/src/crepe/ParticleSystem.cpp index 8891e2b..3a1f653 100644 --- a/src/crepe/ParticleSystem.cpp +++ b/src/crepe/ParticleSystem.cpp @@ -5,40 +5,32 @@ #include // include iostream for std::cout #include "ParticleEmitter.h" #include "Particle.h" +#include "ComponentManager.h" using namespace crepe; ParticleSystem::ParticleSystem() : m_elapsedTime(0.0f) {} // Initialize m_elapsedTime to 0 -void ParticleSystem::update(float deltaTime, std::vector& emitters) { - // std::cout << "ParticleSystem update" << std::endl; +void ParticleSystem::update(float deltaTime, std::vector>& emitters) { for (ParticleEmitter& emitter : emitters) { float updateAmount = 1/static_cast(emitter.m_emissionRate); for (float i = 0; i < deltaTime; i += updateAmount) { emitParticle(emitter); } - // std::cout << "after emit" << std::endl; - - //update/move particles afterwards delete if not alive. for (size_t j = 0; j < emitter.particles.size(); j++) { - // std::cout << "update" << std::endl; if(emitter.particles[j].active) { emitter.particles[j].update(deltaTime); } } - - } } void ParticleSystem::emitParticle(ParticleEmitter& emitter) { - // std::cout << "new emitter:" << std::endl; - Position initialPosition = { emitter.m_position.x, emitter.m_position.y }; + Position initialPosition = { emitter.m_position.x, emitter.m_position.y}; float randomAngle = 0.0f; - //check if value is overthe 360 degrees if(emitter.m_maxAngle < emitter.m_minAngle) { randomAngle = ((emitter.m_minAngle + (std::rand() % (static_cast(emitter.m_maxAngle + 360 - emitter.m_minAngle + 1))))%360); @@ -47,8 +39,6 @@ void ParticleSystem::emitParticle(ParticleEmitter& emitter) { { randomAngle = emitter.m_minAngle + (std::rand() % (static_cast(emitter.m_maxAngle - emitter.m_minAngle + 1))); } - - float angleInRadians = randomAngle * (M_PI / 180.0f); float randomSpeedOffset = (static_cast(std::rand()) / RAND_MAX) * (2 * emitter.m_speedOffset) - emitter.m_speedOffset; float velocityX = (emitter.m_speed + randomSpeedOffset) * std::cos(angleInRadians); @@ -57,23 +47,12 @@ void ParticleSystem::emitParticle(ParticleEmitter& emitter) { velocityX, velocityY }; - // std::cout << "emitter.m_endLifespan:" << emitter.m_endLifespan << std::endl; for (size_t i = 0; i < emitter.particles.size(); i++) { if(!emitter.particles[i].active) { - // std::cout << "active " << emitter.particles[i].active << std::endl; - // std::cout << "lifespan " << emitter.particles[i].lifespan << std::endl; - // std::cout << "timeInLife " << emitter.particles[i].timeInLife << std::endl; - // std::cout << "emitter.m_endLifespan" << emitter.m_endLifespan << std::endl; - // std::cout << "initialPositionx" << initialPosition.x << std::endl; - // std::cout << "initialPositiony" << initialPosition.y << std::endl; - // std::cout << "initialVelocityx" << initialVelocity.x << std::endl; - // std::cout << "initialVelocityy" << initialVelocity.y << std::endl; emitter.particles[i].reset(emitter.m_endLifespan, initialPosition, initialVelocity); break; } } - - //emitter.particles.emplace_back(emitter.m_endLifespan, initialPosition, initialVelocity); } diff --git a/src/crepe/ParticleSystem.h b/src/crepe/ParticleSystem.h index fd6d110..7b14f71 100644 --- a/src/crepe/ParticleSystem.h +++ b/src/crepe/ParticleSystem.h @@ -9,7 +9,7 @@ namespace crepe { class ParticleSystem { public: ParticleSystem(); - void update(float deltaTime, std::vector& emitters); + void update(float deltaTime, std::vector>& emitters); private: void emitParticle(ParticleEmitter &emitter); //emits a new particle diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 36ceba1..cbf8e31 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -15,5 +15,5 @@ endfunction() add_example(audio_internal) add_example(components_internal) add_example(script) -add_example(particel) -target_link_libraries(particel PUBLIC SDL2) +add_example(particle) +target_link_libraries(particle PUBLIC SDL2) diff --git a/src/example/particel.cpp b/src/example/particel.cpp deleted file mode 100644 index c66bbef..0000000 --- a/src/example/particel.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include -#include "SDLApp.h" -#include "ParticleEmitter.h" -#include "ParticleSystem.h" -#include "Particle.h" -#include - -using namespace crepe; - -const int WINDOW_WIDTH = 800; -const int WINDOW_HEIGHT = 600; - -int main(int argc, char* argv[]) { - SDLApp app(WINDOW_WIDTH, WINDOW_HEIGHT); - - if (!app.initialize()) { - std::cerr << "Failed to initialize SDLApp." << std::endl; - return 1; - } - - ParticleSystem particleSystem; - - unsigned int maxParticles = 100; // maximum number of particles - unsigned int emissionRate = 1; // particles created per second - unsigned int speed = 50; // base speed of particles - unsigned int speedOffset = 10; // random offset for particle speed - unsigned int angle = 90; // base angle of particle emission - unsigned int angleOffset = 30; // random offset for particle angle - float beginLifespan = 0.0f; // beginning lifespan of particles - float endLifespan = 2.0f; // ending lifespan of particles - - // Vector to hold all the emitters - std::vector emitters; - - // Loop to create 1000 emitters - for (unsigned int i = 0; i < 1000; ++i) { - ParticleEmitter emitter(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset, beginLifespan, endLifespan); - - // Set a position for each emitter, modifying the position for demonstration - emitter.m_position = {static_cast(200 + (i % 100)), static_cast(200 + (i / 100) * 10)}; // Adjust position for each emitter - - emitters.push_back(emitter); // Add the emitter to the vector - } - float deltaTime = 0.1f; - bool running = true; - std::cout << "start loop " << std::endl; - while (running) { - app.handleEvents(running); - - // Start timing - auto start = std::chrono::high_resolution_clock::now(); - - particleSystem.update(deltaTime, emitters); // update particle system with delta time and emitters - - // End timing - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration duration = end - start; // get duration in milliseconds - - std::cout << "Update took " << duration.count() << " ms" << std::endl; - app.clearScreen(); - - start = std::chrono::high_resolution_clock::now(); - // render particles using the drawSquare method from SDLApp - for (const ParticleEmitter& emitter : emitters) { - for (const Particle& particle : emitter.particles) { - if(particle.active)app.drawSquare(particle.position.x, particle.position.y, 5); // draw each particle - } - } - - - app.presentScreen(); - end = std::chrono::high_resolution_clock::now(); - duration = end - start; // get duration in milliseconds - - std::cout << "screen took " << duration.count() << " ms" << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(20)); // simulate ~50 FPS - } - - app.cleanUp(); - return 0; -} diff --git a/src/example/particle.cpp b/src/example/particle.cpp new file mode 100644 index 0000000..fc56230 --- /dev/null +++ b/src/example/particle.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include "SDLApp.h" +#include "ParticleEmitter.h" +#include "ParticleSystem.h" +#include "Particle.h" +#include +#include +#include +#include + +using namespace crepe; +using namespace std; + +const int WINDOW_WIDTH = 800; +const int WINDOW_HEIGHT = 600; + +int main(int argc, char* argv[]) { + SDLApp app(WINDOW_WIDTH, WINDOW_HEIGHT); + + if (!app.initialize()) { + cerr << "Failed to initialize SDLApp." << endl; + return 1; + } + + auto & mgr = ComponentManager::get_instance(); + GameObject * game_object[1]; + game_object[0] = new GameObject(0, "Name", "Tag", 0); + + + ParticleSystem particleSystem; + + unsigned int maxParticles = 100; // maximum number of particles + unsigned int emissionRate = 1; // particles created per second + unsigned int speed = 50; // base speed of particles + unsigned int speedOffset = 10; // random offset for particle speed + unsigned int angle = 90; // base angle of particle emission + unsigned int angleOffset = 30; // random offset for particle angle + float beginLifespan = 0.0f; // beginning lifespan of particles + float endLifespan = 2.0f; // ending lifespan of particles + + // Vector to hold all the emitters + // vector emitters; + game_object[0]->add_component(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset, beginLifespan, endLifespan); + + std::vector> emitters = mgr.get_components_by_type(); + + + // Loop to create 1000 emitters + // for (unsigned int i = 0; i < 1000; ++i) { + // ParticleEmitter emitter(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset, beginLifespan, endLifespan); + + // // Set a position for each emitter, modifying the position for demonstration + // emitter.m_position = {static_cast(200 + (i % 100)), static_cast(200 + (i / 100) * 10)}; // Adjust position for each emitter + + // emitters.push_back(emitter); // Add the emitter to the vector + // } + float deltaTime = 0.1f; + bool running = true; + cout << "start loop " << endl; + while (running) { + app.handleEvents(running); + + // Start timing + auto start = chrono::high_resolution_clock::now(); + + particleSystem.update(deltaTime, emitters); // update particle system with delta time and emitters + + // End timing + auto end = chrono::high_resolution_clock::now(); + chrono::duration duration = end - start; // get duration in milliseconds + + cout << "Update took " << duration.count() << " ms" << endl; + app.clearScreen(); + + start = chrono::high_resolution_clock::now(); + // render particles using the drawSquare method from SDLApp + for (const ParticleEmitter& emitter : emitters) { + for (const Particle& particle : emitter.particles) { + if(particle.active)app.drawSquare(particle.position.x, particle.position.y, 5); // draw each particle + } + } + + + app.presentScreen(); + end = chrono::high_resolution_clock::now(); + duration = end - start; // get duration in milliseconds + + cout << "screen took " << duration.count() << " ms" << endl; + + this_thread::sleep_for(chrono::milliseconds(20)); // simulate ~50 FPS + } + + app.cleanUp(); + return 0; +} -- cgit v1.2.3 From 2f644ac353f65cd182be13549e32e9b9409f7aad Mon Sep 17 00:00:00 2001 From: max-001 Date: Thu, 10 Oct 2024 13:20:35 +0200 Subject: Clang-format --- mwe/ecs-homemade/inc/ComponentManager.h | 43 +++++---- mwe/ecs-homemade/inc/ComponentManager.hpp | 154 +++++++++++++++++++----------- mwe/ecs-homemade/inc/GameObjectMax.h | 3 +- mwe/ecs-homemade/inc/GameObjectMax.hpp | 5 +- mwe/ecs-homemade/src/ComponentManager.cpp | 20 ++-- mwe/ecs-homemade/src/Components.cpp | 3 +- mwe/ecs-homemade/src/GameObjectMax.cpp | 4 +- mwe/ecs-homemade/src/main.cpp | 35 ++++--- 8 files changed, 161 insertions(+), 106 deletions(-) diff --git a/mwe/ecs-homemade/inc/ComponentManager.h b/mwe/ecs-homemade/inc/ComponentManager.h index 1a58b01..d368516 100644 --- a/mwe/ecs-homemade/inc/ComponentManager.h +++ b/mwe/ecs-homemade/inc/ComponentManager.h @@ -2,39 +2,44 @@ #include "Components.h" #include -#include -#include -#include #include +#include +#include #include +#include class ComponentManager { public: - static ComponentManager& GetInstance(); //Singleton + static ComponentManager & GetInstance(); //Singleton - ComponentManager(const ComponentManager&) = delete; //Singleton - ComponentManager(ComponentManager&&) = delete; //Singleton - ComponentManager& operator=(const ComponentManager&) = delete; //Singleton - ComponentManager& operator=(ComponentManager&&) = delete; //Singleton + ComponentManager(const ComponentManager &) = delete; //Singleton + ComponentManager(ComponentManager &&) = delete; //Singleton + ComponentManager & operator=(const ComponentManager &) = delete; //Singleton + ComponentManager & operator=(ComponentManager &&) = delete; //Singleton template - void AddComponent(std::uint32_t id, Args&&... args); //Add a component of a specific type + void AddComponent(std::uint32_t id, + Args &&... args); //Add a component of a specific type template - void DeleteComponentsById(std::uint32_t id); //Deletes all components of a specific type and id + void DeleteComponentsById( + std::uint32_t id); //Deletes all components of a specific type and id template - void DeleteComponents(); //Deletes all components of a specific type - void DeleteAllComponentsOfId(std::uint32_t id); //Deletes all components of a specific id - void DeleteAllComponents(); //Deletes all components + void DeleteComponents(); //Deletes all components of a specific type + void DeleteAllComponentsOfId( + std::uint32_t id); //Deletes all components of a specific id + void DeleteAllComponents(); //Deletes all components template - std::vector> GetComponentsByID(std::uint32_t id) const; //Get a vector<> of all components at specific type and id + std::vector> GetComponentsByID(std::uint32_t id) + const; //Get a vector<> of all components at specific type and id template - std::vector> GetComponentsByType() const; //Get a vector<> of all components of a specific type + std::vector> GetComponentsByType() + const; //Get a vector<> of all components of a specific type private: - static ComponentManager mInstance; //Singleton + static ComponentManager mInstance; //Singleton - ComponentManager(); //Singleton + ComponentManager(); //Singleton /* * The std::unordered_map>>> below might seem a bit strange, let me explain this structure: @@ -42,7 +47,9 @@ private: * The first std::vector<> stores another vector<>. This first vector<> is to bind the entity's id to a component. * The second std::vector<> stores unique_ptrs. Each component can be gathered via an unique_ptr. This second vector<> allows multiple components of the same std::type_index for one entity (id). */ - std::unordered_map>>> mComponents; + std::unordered_map>>> + mComponents; }; #include "ComponentManager.hpp" diff --git a/mwe/ecs-homemade/inc/ComponentManager.hpp b/mwe/ecs-homemade/inc/ComponentManager.hpp index 720ee79..161ce71 100644 --- a/mwe/ecs-homemade/inc/ComponentManager.hpp +++ b/mwe/ecs-homemade/inc/ComponentManager.hpp @@ -1,88 +1,126 @@ template -void ComponentManager::AddComponent(std::uint32_t id, Args&&... args) { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - if (mComponents.find(type) == mComponents.end()) { //Check if this component type is already in the unordered_map<> - mComponents[type] = std::vector>>(); //If not, create a new (empty) vector<> of vector> - } +void ComponentManager::AddComponent(std::uint32_t id, Args &&... args) { + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) + + if (mComponents.find(type) + == mComponents + .end()) { //Check if this component type is already in the unordered_map<> + mComponents[type] = std::vector>>(); //If not, create a new (empty) vector<> of vector> + } - if (id >= mComponents[type].size()) { //Resize the vector<> if the id is greater than the current size - mComponents[type].resize(id + 1); //Initialize new slots to nullptr (resize does automatically init to nullptr) - } + if (id + >= mComponents[type] + .size()) { //Resize the vector<> if the id is greater than the current size + mComponents[type].resize( + id + + 1); //Initialize new slots to nullptr (resize does automatically init to nullptr) + } - mComponents[type][id].push_back(std::make_unique(std::forward(args)...)); //Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> + mComponents[type][id].push_back(std::make_unique(std::forward( + args)...)); //Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> } template void ComponentManager::DeleteComponentsById(std::uint32_t id) { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - std::vector>>& componentArray = mComponents[type]; //Get the correct vector<> - - if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - componentArray[id].clear(); //Clear the whole vector<> of this specific type and id - } + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) + + if (mComponents.find(type) + != mComponents.end()) { //Find the type (in the unordered_map<>) + std::vector>> & componentArray + = mComponents[type]; //Get the correct vector<> + + if (id + < componentArray + .size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + componentArray[id] + .clear(); //Clear the whole vector<> of this specific type and id + } } } -template -void ComponentManager::DeleteComponents() { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) +template void ComponentManager::DeleteComponents() { + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - mComponents[type].clear(); //Clear the whole vector<> of this specific type + if (mComponents.find(type) + != mComponents.end()) { //Find the type (in the unordered_map<>) + mComponents[type] + .clear(); //Clear the whole vector<> of this specific type } } template -std::vector> ComponentManager::GetComponentsByID(std::uint32_t id) const { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) - - std::vector> componentVector; //Create an empty vector<> - - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) - - const std::vector>>& componentArray = mComponents.at(type); //Get the correct vector<> - - if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - for (const std::unique_ptr& componentPtr : componentArray[id]) { //Loop trough the whole vector<> - T* castedComponent = static_cast(componentPtr.get()); //Cast the unique_ptr to a raw pointer - - if (castedComponent) { //Ensure that the cast was successful - componentVector.push_back(*castedComponent); //Add the dereferenced raw pointer to the vector<> - } - } - } - } +std::vector> +ComponentManager::GetComponentsByID(std::uint32_t id) const { + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) + + std::vector> + componentVector; //Create an empty vector<> + + if (mComponents.find(type) + != mComponents.end()) { //Find the type (in the unordered_map<>) + + const std::vector>> & + componentArray + = mComponents.at(type); //Get the correct vector<> + + if (id + < componentArray + .size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + for (const std::unique_ptr & componentPtr : + componentArray[id]) { //Loop trough the whole vector<> + T * castedComponent = static_cast( + componentPtr.get()); //Cast the unique_ptr to a raw pointer + + if (castedComponent) { //Ensure that the cast was successful + componentVector.push_back( + *castedComponent); //Add the dereferenced raw pointer to the vector<> + } + } + } + } - return componentVector; //Return the vector<> + return componentVector; //Return the vector<> } template -std::vector> ComponentManager::GetComponentsByType() const { - std::type_index type = typeid(T); //Determine the type of T (this is used as the key of the unordered_map<>) +std::vector> +ComponentManager::GetComponentsByType() const { + std::type_index type = typeid( + T); //Determine the type of T (this is used as the key of the unordered_map<>) - std::vector> componentVector; //Create an empty vector<> + std::vector> + componentVector; //Create an empty vector<> //std::uint32_t id = 0; //Set the id to 0 (the id will also be stored in the returned vector<>) - if (mComponents.find(type) != mComponents.end()) { //Find the type (in the unordered_map<>) + if (mComponents.find(type) + != mComponents.end()) { //Find the type (in the unordered_map<>) - const std::vector>>& componentArray = mComponents.at(type); //Get the correct vector<> + const std::vector>> & + componentArray + = mComponents.at(type); //Get the correct vector<> - for (const std::vector>& component : componentArray) { //Loop through the whole vector<> - for (const std::unique_ptr& componentPtr : component) { //Loop trough the whole vector<> - T* castedComponent = static_cast(componentPtr.get()); //Cast the unique_ptr to a raw pointer + for (const std::vector> & component : + componentArray) { //Loop through the whole vector<> + for (const std::unique_ptr & componentPtr : + component) { //Loop trough the whole vector<> + T * castedComponent = static_cast( + componentPtr.get()); //Cast the unique_ptr to a raw pointer - if (castedComponent) { //Ensure that the cast was successful - componentVector.emplace_back(std::ref(*castedComponent)); //Pair the dereferenced raw pointer and the id and add it to the vector<> - } - } + if (castedComponent) { //Ensure that the cast was successful + componentVector.emplace_back(std::ref( + *castedComponent)); //Pair the dereferenced raw pointer and the id and add it to the vector<> + } + } //++id; //Increase the id (the id will also be stored in the returned vector<>) - } - } + } + } - return componentVector; //Return the vector<> + return componentVector; //Return the vector<> } diff --git a/mwe/ecs-homemade/inc/GameObjectMax.h b/mwe/ecs-homemade/inc/GameObjectMax.h index f0bcec9..c88a834 100644 --- a/mwe/ecs-homemade/inc/GameObjectMax.h +++ b/mwe/ecs-homemade/inc/GameObjectMax.h @@ -7,8 +7,7 @@ class GameObject { public: GameObject(std::uint32_t id, std::string name, std::string tag, int layer); - template - void AddComponent(Args&&... args); + template void AddComponent(Args &&... args); std::uint32_t mId; std::string mName; diff --git a/mwe/ecs-homemade/inc/GameObjectMax.hpp b/mwe/ecs-homemade/inc/GameObjectMax.hpp index 1e952ba..91d51ea 100644 --- a/mwe/ecs-homemade/inc/GameObjectMax.hpp +++ b/mwe/ecs-homemade/inc/GameObjectMax.hpp @@ -1,6 +1,7 @@ #include "ComponentManager.h" template -void GameObject::AddComponent(Args&&... args) { - ComponentManager::GetInstance().AddComponent(mId, std::forward(args)...); +void GameObject::AddComponent(Args &&... args) { + ComponentManager::GetInstance().AddComponent( + mId, std::forward(args)...); } diff --git a/mwe/ecs-homemade/src/ComponentManager.cpp b/mwe/ecs-homemade/src/ComponentManager.cpp index 16cc2b6..536c152 100644 --- a/mwe/ecs-homemade/src/ComponentManager.cpp +++ b/mwe/ecs-homemade/src/ComponentManager.cpp @@ -2,20 +2,22 @@ ComponentManager ComponentManager::mInstance; -ComponentManager& ComponentManager::GetInstance() { - return mInstance; -} +ComponentManager & ComponentManager::GetInstance() { return mInstance; } ComponentManager::ComponentManager() {} void ComponentManager::DeleteAllComponentsOfId(std::uint32_t id) { - for(auto& [type, componentArray] : mComponents) { //Loop through all the types (in the unordered_map<>) - if (id < componentArray.size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - componentArray[id].clear(); //Clear the components at this specific id - } - } + for (auto & [type, componentArray] : + mComponents) { //Loop through all the types (in the unordered_map<>) + if (id + < componentArray + .size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + componentArray[id] + .clear(); //Clear the components at this specific id + } + } } void ComponentManager::DeleteAllComponents() { - mComponents.clear(); //Clear the whole unordered_map<> + mComponents.clear(); //Clear the whole unordered_map<> } diff --git a/mwe/ecs-homemade/src/Components.cpp b/mwe/ecs-homemade/src/Components.cpp index 69b5eaa..c8347b3 100644 --- a/mwe/ecs-homemade/src/Components.cpp +++ b/mwe/ecs-homemade/src/Components.cpp @@ -5,6 +5,7 @@ Component::Component() : mActive(true) {} Sprite::Sprite(std::string path) : mPath(path) {} -Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} +Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) + : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} Colider::Colider(int size) : mSize(size) {} diff --git a/mwe/ecs-homemade/src/GameObjectMax.cpp b/mwe/ecs-homemade/src/GameObjectMax.cpp index 62c41de..b0c5af7 100644 --- a/mwe/ecs-homemade/src/GameObjectMax.cpp +++ b/mwe/ecs-homemade/src/GameObjectMax.cpp @@ -2,4 +2,6 @@ #include "ComponentManager.h" -GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, int layer) : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} +GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, + int layer) + : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} diff --git a/mwe/ecs-homemade/src/main.cpp b/mwe/ecs-homemade/src/main.cpp index 41f7d6d..330e154 100644 --- a/mwe/ecs-homemade/src/main.cpp +++ b/mwe/ecs-homemade/src/main.cpp @@ -1,54 +1,59 @@ +#include +#include #include #include -#include -#include #include "ComponentManager.h" -#include "GameObjectMax.h" #include "Components.h" +#include "GameObjectMax.h" int main() { auto startAdding = std::chrono::high_resolution_clock::now(); - GameObject* gameObject[100000]; + GameObject * gameObject[100000]; - for(int i = 0; i < 100000; ++i) { + for (int i = 0; i < 100000; ++i) { gameObject[i] = new GameObject(i, "Name", "Tag", 0); gameObject[i]->AddComponent("C:/Test"); gameObject[i]->AddComponent(0, 0, i); gameObject[i]->AddComponent(i); } - + auto stopAdding = std::chrono::high_resolution_clock::now(); //This is what systems would do: - std::vector> sprites = ComponentManager::GetInstance().GetComponentsByType(); - for(Sprite& sprite : sprites) { + std::vector> sprites + = ComponentManager::GetInstance().GetComponentsByType(); + for (Sprite & sprite : sprites) { //std::cout << sprite.get().mPath << std::endl; } //std::cout << std::endl; - std::vector> rigidBodies = ComponentManager::GetInstance().GetComponentsByType(); - for(Rigidbody& rigidbody : rigidBodies) { + std::vector> rigidBodies + = ComponentManager::GetInstance().GetComponentsByType(); + for (Rigidbody & rigidbody : rigidBodies) { //std::cout << rigidbody.get().mMass << " " << rigidbody.get().mGravityScale << " " << rigidbody.get().mBodyType << std::endl; } //std::cout << std::endl; - std::vector> coliders = ComponentManager::GetInstance().GetComponentsByType(); - for(Colider& colider : coliders) { + std::vector> coliders + = ComponentManager::GetInstance().GetComponentsByType(); + for (Colider & colider : coliders) { //std::cout << colider.get().mSize << std::endl; } auto stopLooping = std::chrono::high_resolution_clock::now(); for (int i = 0; i < 100000; ++i) { - delete gameObject[i]; + delete gameObject[i]; } - auto Addtime = std::chrono::duration_cast(stopAdding - startAdding); - auto LoopTime = std::chrono::duration_cast(stopLooping - stopAdding); + auto Addtime = std::chrono::duration_cast( + stopAdding - startAdding); + auto LoopTime = std::chrono::duration_cast( + stopLooping - stopAdding); std::cout << "AddTime: " << Addtime.count() << " us" << std::endl; std::cout << "LoopTime: " << LoopTime.count() << " us" << std::endl; } -- cgit v1.2.3 From 5d041cce13da66aa3457d236579a9ac90ebf7618 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 12 Oct 2024 20:54:07 +0200 Subject: fix constructor/destructor member visibility for component manager --- src/crepe/Component.cpp | 1 - src/crepe/Component.h | 6 ++- src/crepe/ComponentManager.cpp | 12 +++++- src/crepe/ComponentManager.h | 3 +- src/crepe/ComponentManager.hpp | 86 +++++++++++++++++++----------------------- src/crepe/api/BehaviorScript.h | 7 +++- src/crepe/util/log.h | 2 +- 7 files changed, 61 insertions(+), 56 deletions(-) diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp index d14159c..737f30a 100644 --- a/src/crepe/Component.cpp +++ b/src/crepe/Component.cpp @@ -2,4 +2,3 @@ using namespace crepe; -Component::Component() : active(true) {} diff --git a/src/crepe/Component.h b/src/crepe/Component.h index 16a4ce5..00b2164 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -3,12 +3,14 @@ namespace crepe { class Component { +protected: + Component() = default; public: - Component(); + virtual ~Component() = default; // TODO: shouldn't this constructor be deleted because this class will never // directly be instantiated? - bool active; + bool active = true; }; } // namespace crepe diff --git a/src/crepe/ComponentManager.cpp b/src/crepe/ComponentManager.cpp index 9a3fec7..8aa9d4e 100644 --- a/src/crepe/ComponentManager.cpp +++ b/src/crepe/ComponentManager.cpp @@ -1,3 +1,4 @@ +#include "util/log.h" #include "ComponentManager.h" using namespace crepe; @@ -20,5 +21,14 @@ void ComponentManager::delete_all_components_of_id(uint32_t id) { void ComponentManager::delete_all_components() { // Clear the whole unordered_map<> - components.clear(); + this->components.clear(); } + +ComponentManager::ComponentManager() { + dbg_trace(); +} + +ComponentManager::~ComponentManager() { + dbg_trace(); +} + diff --git a/src/crepe/ComponentManager.h b/src/crepe/ComponentManager.h index 2ab9dc8..eab9b45 100644 --- a/src/crepe/ComponentManager.h +++ b/src/crepe/ComponentManager.h @@ -44,7 +44,8 @@ public: std::vector> get_components_by_type() const; private: - ComponentManager() = default; + ComponentManager(); + virtual ~ComponentManager(); /* * The std::unordered_map>>> below might seem a bit strange, let me explain this structure: diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp index 084cd33..8fc1cba 100644 --- a/src/crepe/ComponentManager.hpp +++ b/src/crepe/ComponentManager.hpp @@ -60,11 +60,9 @@ void ComponentManager::delete_components() { // Determine the type of T (this is used as the key of the unordered_map<>) std::type_index type = typeid(T); - // Find the type (in the unordered_map<>) - if (components.find(type) != components.end()) { - // Clear the whole vector<> of this specific type - components[type].clear(); - } + if (components.find(type) == components.end()) return; + + components[type].clear(); } template @@ -78,30 +76,25 @@ ComponentManager::get_components_by_id(uint32_t id) const { // Create an empty vector<> vector> component_vector; - // Find the type (in the unordered_map<>) - if (components.find(type) != components.end()) { - // Get the correct vector<> - const vector>> & component_array - = components.at(type); + if (components.find(type) == components.end()) return component_vector; - // Make sure that the id (that we are looking for) is within the boundaries of the vector<> - if (id < component_array.size()) { - // Loop trough the whole vector<> - for (const unique_ptr & component_ptr : - component_array[id]) { - // Cast the unique_ptr to a raw pointer - T * casted_component = static_cast(component_ptr.get()); - - // Ensure that the cast was successful - if (casted_component) { - // Add the dereferenced raw pointer to the vector<> - component_vector.push_back(*casted_component); - } - } - } + // Get the correct vector<> + const vector>> & component_array = components.at(type); + + // Make sure that the id (that we are looking for) is within the boundaries of the vector<> + if (id >= component_array.size()) return component_vector; + + // Loop trough the whole vector<> + for (const unique_ptr & component_ptr : component_array[id]) { + // Cast the unique_ptr to a raw pointer + T * casted_component = static_cast(component_ptr.get()); + + if (casted_component == nullptr) continue; + + // Add the dereferenced raw pointer to the vector<> + component_vector.push_back(*casted_component); } - // Return the vector<> return component_vector; } @@ -119,30 +112,27 @@ ComponentManager::get_components_by_type() const { // uint32_t id = 0; // Find the type (in the unordered_map<>) - if (components.find(type) != components.end()) { + if (components.find(type) == components.end()) return component_vector; - // Get the correct vector<> - const vector>> & component_array - = components.at(type); - - // Loop through the whole vector<> - for (const vector> & component : - component_array) { - // Loop trough the whole vector<> - for (const unique_ptr & component_ptr : component) { - // Cast the unique_ptr to a raw pointer - T * casted_component = static_cast(component_ptr.get()); - - // Ensure that the cast was successful - if (casted_component) { - // Pair the dereferenced raw pointer and the id and add it to the vector<> - component_vector.emplace_back(ref(*casted_component)); - } - } - - // Increase the id (the id will also be stored in the returned vector<>) - //++id; + // Get the correct vector<> + const vector>> & component_array = components.at(type); + + // Loop through the whole vector<> + for (const vector> & component : component_array) { + // Loop trough the whole vector<> + for (const unique_ptr & component_ptr : component) { + // Cast the unique_ptr to a raw pointer + T * casted_component = static_cast(component_ptr.get()); + + // Ensure that the cast was successful + if (casted_component == nullptr) continue; + + // Pair the dereferenced raw pointer and the id and add it to the vector<> + component_vector.emplace_back(ref(*casted_component)); } + + // Increase the id (the id will also be stored in the returned vector<>) + //++id; } // Return the vector<> diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index e9542c1..ba60a8c 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -6,11 +6,14 @@ namespace crepe::api { class BehaviorScript : public Script, public Component { +protected: // only allow ComponentManager to instantiate scripts friend class ComponentManager; - -protected: BehaviorScript(); +public: + // but allow uniqe_ptr to call the destructor (THIS IS VERY IMPORTANT) + virtual ~BehaviorScript() = default; + }; } diff --git a/src/crepe/util/log.h b/src/crepe/util/log.h index 2b0fbe1..fa5f633 100644 --- a/src/crepe/util/log.h +++ b/src/crepe/util/log.h @@ -15,7 +15,7 @@ // very illegal global function-style macros // NOLINTBEGIN #define dbg_logf(fmt, ...) _crepe_logf_here(": " fmt, __VA_ARGS__) -#define dbg_log(str) _crepe_logf_here(": %s", str) +#define dbg_log(str) _crepe_logf_here("%s: " str, "") #define dbg_trace() _crepe_logf_here("%s", "") // NOLINTEND -- cgit v1.2.3 From f9aa198eef7b85eeba3bac4c4fe2d4578b836bbf Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sun, 13 Oct 2024 20:52:08 +0200 Subject: WIP behavior script (problems) --- src/crepe/ComponentManager.hpp | 1 + src/crepe/ScriptSystem.cpp | 11 +++++++++++ src/crepe/api/BehaviorScript.h | 1 + src/crepe/util/CMakeLists.txt | 2 ++ src/crepe/util/fmt.cpp | 34 ++++++++++++++++++++++++++++++++++ src/crepe/util/fmt.h | 10 ++++++++++ src/crepe/util/log.cpp | 27 +++++++++------------------ src/crepe/util/log.h | 2 +- src/example/script.cpp | 3 ++- 9 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 src/crepe/util/fmt.cpp create mode 100644 src/crepe/util/fmt.h diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp index 8fc1cba..c872594 100644 --- a/src/crepe/ComponentManager.hpp +++ b/src/crepe/ComponentManager.hpp @@ -3,6 +3,7 @@ #include #include "ComponentManager.h" +#include "api/BehaviorScript.h" namespace crepe { diff --git a/src/crepe/ScriptSystem.cpp b/src/crepe/ScriptSystem.cpp index e301c71..e40909e 100644 --- a/src/crepe/ScriptSystem.cpp +++ b/src/crepe/ScriptSystem.cpp @@ -1,3 +1,7 @@ +#include +#include "ComponentManager.h" +#include "api/BehaviorScript.h" +#include "util/fmt.h" #include "util/log.h" #include "ScriptSystem.h" @@ -17,6 +21,13 @@ ScriptSystem & ScriptSystem::get_instance() { } void ScriptSystem::update() { + using namespace std; dbg_trace(); + + ComponentManager & mgr = ComponentManager::get_instance(); + vector> scripts = mgr.get_components_by_type(); + + dbg_logf("script count: %lu", scripts.size()); + } diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index ba60a8c..dd04dc0 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -14,6 +14,7 @@ public: // but allow uniqe_ptr to call the destructor (THIS IS VERY IMPORTANT) virtual ~BehaviorScript() = default; + static BehaviorScript * component; }; } diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index 100f028..e2cffaf 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -1,9 +1,11 @@ target_sources(crepe PUBLIC log.cpp + fmt.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES color.h log.h + fmt.h ) diff --git a/src/crepe/util/fmt.cpp b/src/crepe/util/fmt.cpp new file mode 100644 index 0000000..3a43a6f --- /dev/null +++ b/src/crepe/util/fmt.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +#include "fmt.h" + +using namespace std; + +string crepe::util::va_stringf(va_list args, const char * fmt) { + va_list args_copy; + va_copy(args_copy, args); + + size_t sz = vsnprintf(NULL, 0, fmt, args_copy) + 1; + char * msg = (char *) malloc(sz); + va_end(args_copy); + + vsnprintf(msg, sz, fmt, args); + + string out = msg; + free(msg); + + va_end(args); + + return out; +} + +string crepe::util::stringf(const char * fmt, ...) { + va_list args; + va_start(args, fmt); + string out = va_stringf(args, fmt); + va_end(args); + return out; +} + diff --git a/src/crepe/util/fmt.h b/src/crepe/util/fmt.h new file mode 100644 index 0000000..5c45703 --- /dev/null +++ b/src/crepe/util/fmt.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace crepe::util { + +std::string va_stringf(va_list args, const char * fmt); +std::string stringf(const char * fmt, ...); + +} diff --git a/src/crepe/util/log.cpp b/src/crepe/util/log.cpp index f91d52c..b46620d 100644 --- a/src/crepe/util/log.cpp +++ b/src/crepe/util/log.cpp @@ -4,6 +4,7 @@ #include #include "log.h" +#include "fmt.h" using namespace crepe::util; @@ -14,37 +15,27 @@ static const char * const LOG_PREFIX[] = { [log_level::ERROR] = "[ERR] ", }; -static void va_logf(enum log_level level, va_list args, const std::string fmt) { - va_list args_copy; - va_copy(args_copy, args); - - // prepend log level and ensure newline - std::string format_fixed = LOG_PREFIX[level] + fmt; - if (!format_fixed.ends_with("\n")) format_fixed += "\n"; - - size_t sz = vsnprintf(NULL, 0, format_fixed.c_str(), args_copy) + 1; - char * msg = (char *) malloc(sz); - va_end(args_copy); - - vsnprintf(msg, sz, format_fixed.c_str(), args); +static void log(enum log_level level, const std::string msg) { + using namespace std; + string final = string(LOG_PREFIX[level]) + msg; + if (!final.ends_with("\n")) final += "\n"; // TODO: also log to file or smth - printf("%s", msg); + printf("%s", final.c_str()); fflush(stdout); - - free(msg); } void crepe::util::logf(const char * fmt, ...) { va_list args; va_start(args, fmt); - va_logf(crepe::util::log_level::DEBUG, args, fmt); + log(log_level::DEBUG, va_stringf(args, fmt)); va_end(args); } void crepe::util::logf(log_level level, const char * fmt, ...) { va_list args; va_start(args, fmt); - va_logf(level, args, fmt); + log(level, va_stringf(args, fmt)); va_end(args); } + diff --git a/src/crepe/util/log.h b/src/crepe/util/log.h index fa5f633..3e36f0a 100644 --- a/src/crepe/util/log.h +++ b/src/crepe/util/log.h @@ -7,7 +7,7 @@ // utility macros #define _crepe_logf_here(fmt, ...) \ - crepe::util::logf(util::log_level::DEBUG, "%s%s (%s:%d)" fmt "\n", \ + crepe::util::logf(util::log_level::DEBUG, "%s%s (%s:%d)%s" fmt "\n", \ crepe::util::color::FG_WHITE, __PRETTY_FUNCTION__, \ __FILE_NAME__, __LINE__, crepe::util::color::RESET, \ __VA_ARGS__) diff --git a/src/example/script.cpp b/src/example/script.cpp index 28605c7..b8b3c8d 100644 --- a/src/example/script.cpp +++ b/src/example/script.cpp @@ -11,9 +11,10 @@ #include using namespace crepe; +using namespace crepe::api; using namespace std; -class MyScript : public api::BehaviorScript { +class MyScript : public BehaviorScript { void update() { dbg_trace(); } -- cgit v1.2.3 From d9889e4501c1f3ebd649b81816e80d1b40d14c87 Mon Sep 17 00:00:00 2001 From: max-001 Date: Wed, 16 Oct 2024 13:15:31 +0200 Subject: Fixed merge issue --- mwe/ecs-homemade/CMakeLists.txt | 17 +++++++++++++++++ mwe/ecs-homemade/inc/Components.h | 33 +++++++++++++++++++++++++++++++++ mwe/ecs-homemade/inc/GameObjectMax.h | 20 ++++++++++++++++++++ mwe/ecs-homemade/src/Components.cpp | 11 +++++++++++ mwe/ecs-homemade/src/GameObjectMax.cpp | 7 +++++++ 5 files changed, 88 insertions(+) create mode 100644 mwe/ecs-homemade/CMakeLists.txt create mode 100644 mwe/ecs-homemade/inc/Components.h create mode 100644 mwe/ecs-homemade/inc/GameObjectMax.h create mode 100644 mwe/ecs-homemade/src/Components.cpp create mode 100644 mwe/ecs-homemade/src/GameObjectMax.cpp diff --git a/mwe/ecs-homemade/CMakeLists.txt b/mwe/ecs-homemade/CMakeLists.txt new file mode 100644 index 0000000..6267c1a --- /dev/null +++ b/mwe/ecs-homemade/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) +project(ecs-homemade) + +# Set the C++ standard (optional, but good practice) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Use the debug mode (otherwise breakpoints are not compiled) +set(CMAKE_BUILD_TYPE Debug) + +add_executable(ecs-homemade + src/main.cpp + src/ComponentManager.cpp + src/Components.cpp + src/GameObjectMax.cpp +) +target_include_directories(ecs-homemade PRIVATE "${CMAKE_SOURCE_DIR}/inc") diff --git a/mwe/ecs-homemade/inc/Components.h b/mwe/ecs-homemade/inc/Components.h new file mode 100644 index 0000000..98c5fe7 --- /dev/null +++ b/mwe/ecs-homemade/inc/Components.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +class Component { +public: + Component(); + + bool mActive; +}; + +class Sprite : public Component { +public: + Sprite(std::string path); + + std::string mPath; +}; + +class Rigidbody : public Component { +public: + Rigidbody(int mass, int gravityScale, int bodyType); + + int mMass; + int mGravityScale; + int mBodyType; +}; + +class Colider : public Component { +public: + Colider(int size); + + int mSize; +}; diff --git a/mwe/ecs-homemade/inc/GameObjectMax.h b/mwe/ecs-homemade/inc/GameObjectMax.h new file mode 100644 index 0000000..3029053 --- /dev/null +++ b/mwe/ecs-homemade/inc/GameObjectMax.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +class GameObject { +public: + GameObject(std::uint32_t id, std::string name, std::string tag, int layer); + + template + void AddComponent(Args &&... args); + + std::uint32_t mId; + std::string mName; + std::string mTag; + bool mActive; + int mLayer; +}; + +#include "GameObjectMax.hpp" diff --git a/mwe/ecs-homemade/src/Components.cpp b/mwe/ecs-homemade/src/Components.cpp new file mode 100644 index 0000000..c8347b3 --- /dev/null +++ b/mwe/ecs-homemade/src/Components.cpp @@ -0,0 +1,11 @@ +#include "Components.h" +#include + +Component::Component() : mActive(true) {} + +Sprite::Sprite(std::string path) : mPath(path) {} + +Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) + : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} + +Colider::Colider(int size) : mSize(size) {} diff --git a/mwe/ecs-homemade/src/GameObjectMax.cpp b/mwe/ecs-homemade/src/GameObjectMax.cpp new file mode 100644 index 0000000..b0c5af7 --- /dev/null +++ b/mwe/ecs-homemade/src/GameObjectMax.cpp @@ -0,0 +1,7 @@ +#include "GameObjectMax.h" + +#include "ComponentManager.h" + +GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, + int layer) + : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} -- cgit v1.2.3 From 018f6209378a0c30c1e44fba2f80888553b9f67c Mon Sep 17 00:00:00 2001 From: max-001 Date: Wed, 16 Oct 2024 16:50:59 +0200 Subject: Added functionality for scripts --- mwe/ecs-homemade/inc/ComponentManager.h | 2 +- mwe/ecs-homemade/inc/ComponentManager.hpp | 4 +++- mwe/ecs-homemade/inc/Components.h | 33 +++++++++++++++++++++++++++++++ mwe/ecs-homemade/inc/Components.hpp | 20 +++++++++++++++++++ mwe/ecs-homemade/inc/GameObjectMax.h | 2 +- mwe/ecs-homemade/inc/GameObjectMax.hpp | 4 ++-- mwe/ecs-homemade/src/Components.cpp | 12 +++++++++++ mwe/ecs-homemade/src/main.cpp | 19 ++++++++++++++++++ 8 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 mwe/ecs-homemade/inc/Components.hpp diff --git a/mwe/ecs-homemade/inc/ComponentManager.h b/mwe/ecs-homemade/inc/ComponentManager.h index d368516..2cdbb66 100644 --- a/mwe/ecs-homemade/inc/ComponentManager.h +++ b/mwe/ecs-homemade/inc/ComponentManager.h @@ -18,7 +18,7 @@ public: ComponentManager & operator=(ComponentManager &&) = delete; //Singleton template - void AddComponent(std::uint32_t id, + T& AddComponent(std::uint32_t id, Args &&... args); //Add a component of a specific type template void DeleteComponentsById( diff --git a/mwe/ecs-homemade/inc/ComponentManager.hpp b/mwe/ecs-homemade/inc/ComponentManager.hpp index d6da8e8..a120ab1 100644 --- a/mwe/ecs-homemade/inc/ComponentManager.hpp +++ b/mwe/ecs-homemade/inc/ComponentManager.hpp @@ -1,6 +1,6 @@ template -void ComponentManager::AddComponent(std::uint32_t id, Args &&... args) { +T& ComponentManager::AddComponent(std::uint32_t id, Args &&... args) { std::type_index type = typeid( T); //Determine the type of T (this is used as the key of the unordered_map<>) @@ -21,6 +21,8 @@ void ComponentManager::AddComponent(std::uint32_t id, Args &&... args) { mComponents[type][id].push_back(std::make_unique(std::forward( args)...)); //Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> + + return static_cast(*mComponents[type][id].back().get()); } template diff --git a/mwe/ecs-homemade/inc/Components.h b/mwe/ecs-homemade/inc/Components.h index 98c5fe7..b392fd0 100644 --- a/mwe/ecs-homemade/inc/Components.h +++ b/mwe/ecs-homemade/inc/Components.h @@ -1,6 +1,7 @@ #pragma once #include +#include class Component { public: @@ -31,3 +32,35 @@ public: int mSize; }; + +class IBehaviour { +public: + virtual ~IBehaviour() = default; + virtual void onStart() = 0; + virtual void onUpdate() = 0; +}; + +template +class BehaviourWrapper : public IBehaviour { +public: + BehaviourWrapper(); + void onStart() override; + void onUpdate() override; + +private: + T instance; +}; + +class BehaviourScript : public Component { +public: + template + void addScript(); + + void onStart(); + void onUpdate(); + +private: + std::unique_ptr behaviour; +}; + +#include "Components.hpp" diff --git a/mwe/ecs-homemade/inc/Components.hpp b/mwe/ecs-homemade/inc/Components.hpp new file mode 100644 index 0000000..85da5a5 --- /dev/null +++ b/mwe/ecs-homemade/inc/Components.hpp @@ -0,0 +1,20 @@ +#include "Components.h" +#include + +template +BehaviourWrapper::BehaviourWrapper() : instance() {} + +template +void BehaviourWrapper::onStart() { + instance.onStart(); +} + +template +void BehaviourWrapper::onUpdate() { + instance.onUpdate(); +} + +template +void BehaviourScript::addScript() { + behaviour = std::make_unique>(); +} diff --git a/mwe/ecs-homemade/inc/GameObjectMax.h b/mwe/ecs-homemade/inc/GameObjectMax.h index 3029053..c6af50a 100644 --- a/mwe/ecs-homemade/inc/GameObjectMax.h +++ b/mwe/ecs-homemade/inc/GameObjectMax.h @@ -8,7 +8,7 @@ public: GameObject(std::uint32_t id, std::string name, std::string tag, int layer); template - void AddComponent(Args &&... args); + T& AddComponent(Args &&... args); std::uint32_t mId; std::string mName; diff --git a/mwe/ecs-homemade/inc/GameObjectMax.hpp b/mwe/ecs-homemade/inc/GameObjectMax.hpp index 91d51ea..4104589 100644 --- a/mwe/ecs-homemade/inc/GameObjectMax.hpp +++ b/mwe/ecs-homemade/inc/GameObjectMax.hpp @@ -1,7 +1,7 @@ #include "ComponentManager.h" template -void GameObject::AddComponent(Args &&... args) { - ComponentManager::GetInstance().AddComponent( +T& GameObject::AddComponent(Args &&... args) { + return ComponentManager::GetInstance().AddComponent( mId, std::forward(args)...); } diff --git a/mwe/ecs-homemade/src/Components.cpp b/mwe/ecs-homemade/src/Components.cpp index c8347b3..e6a4673 100644 --- a/mwe/ecs-homemade/src/Components.cpp +++ b/mwe/ecs-homemade/src/Components.cpp @@ -9,3 +9,15 @@ Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} Colider::Colider(int size) : mSize(size) {} + +void BehaviourScript::onStart() { + if(behaviour) { + behaviour->onStart(); + } +} + +void BehaviourScript::onUpdate() { + if(behaviour) { + behaviour->onUpdate(); + } +} diff --git a/mwe/ecs-homemade/src/main.cpp b/mwe/ecs-homemade/src/main.cpp index 330e154..e4a8bda 100644 --- a/mwe/ecs-homemade/src/main.cpp +++ b/mwe/ecs-homemade/src/main.cpp @@ -7,6 +7,17 @@ #include "Components.h" #include "GameObjectMax.h" +class myScript { +public: + void onStart() { + std::cout << "In onStart" << std::endl; + } + + void onUpdate() { + std::cout << "In onUpdate" << std::endl; + } +}; + int main() { auto startAdding = std::chrono::high_resolution_clock::now(); @@ -18,6 +29,7 @@ int main() { gameObject[i]->AddComponent("C:/Test"); gameObject[i]->AddComponent(0, 0, i); gameObject[i]->AddComponent(i); + gameObject[i]->AddComponent().addScript(); } auto stopAdding = std::chrono::high_resolution_clock::now(); @@ -44,6 +56,13 @@ int main() { //std::cout << colider.get().mSize << std::endl; } + std::vector> scripts + = ComponentManager::GetInstance().GetComponentsByType(); + for (BehaviourScript & script : scripts) { + //script.onStart(); + //script.onUpdate(); + } + auto stopLooping = std::chrono::high_resolution_clock::now(); for (int i = 0; i < 100000; ++i) { -- cgit v1.2.3 From d21afe5b33b4cb3f5cf1917f4d15f402de41a032 Mon Sep 17 00:00:00 2001 From: max-001 Date: Wed, 16 Oct 2024 17:05:46 +0200 Subject: Make format --- mwe/ecs-homemade/inc/ComponentManager.h | 27 +++--- mwe/ecs-homemade/inc/ComponentManager.hpp | 144 ++++++++++++++++-------------- mwe/ecs-homemade/inc/Components.h | 6 +- mwe/ecs-homemade/inc/Components.hpp | 10 +-- mwe/ecs-homemade/inc/GameObjectMax.h | 2 +- mwe/ecs-homemade/inc/GameObjectMax.hpp | 2 +- mwe/ecs-homemade/src/Components.cpp | 4 +- mwe/ecs-homemade/src/main.cpp | 11 +-- src/crepe/ComponentManager.hpp | 3 +- src/crepe/Script.cpp | 5 +- src/crepe/Script.h | 3 +- src/crepe/ScriptSystem.cpp | 13 +-- src/crepe/ScriptSystem.h | 3 +- src/crepe/System.h | 7 +- src/crepe/api/BehaviorScript.cpp | 5 +- src/crepe/api/BehaviorScript.h | 5 +- src/example/script.cpp | 9 +- 17 files changed, 124 insertions(+), 135 deletions(-) diff --git a/mwe/ecs-homemade/inc/ComponentManager.h b/mwe/ecs-homemade/inc/ComponentManager.h index 2cdbb66..0ba358e 100644 --- a/mwe/ecs-homemade/inc/ComponentManager.h +++ b/mwe/ecs-homemade/inc/ComponentManager.h @@ -17,24 +17,27 @@ public: ComponentManager & operator=(const ComponentManager &) = delete; //Singleton ComponentManager & operator=(ComponentManager &&) = delete; //Singleton + //Add a component of a specific type template - T& AddComponent(std::uint32_t id, - Args &&... args); //Add a component of a specific type + T & AddComponent(std::uint32_t id, Args &&... args); + //Deletes all components of a specific type and id template - void DeleteComponentsById( - std::uint32_t id); //Deletes all components of a specific type and id + void DeleteComponentsById(std::uint32_t id); + //Deletes all components of a specific type template - void DeleteComponents(); //Deletes all components of a specific type - void DeleteAllComponentsOfId( - std::uint32_t id); //Deletes all components of a specific id - void DeleteAllComponents(); //Deletes all components + void DeleteComponents(); + //Deletes all components of a specific id + void DeleteAllComponentsOfId(std::uint32_t id); + //Deletes all components + void DeleteAllComponents(); + //Get a vector<> of all components at specific type and id template - std::vector> GetComponentsByID(std::uint32_t id) - const; //Get a vector<> of all components at specific type and id + std::vector> + GetComponentsByID(std::uint32_t id) const; + //Get a vector<> of all components of a specific type template - std::vector> GetComponentsByType() - const; //Get a vector<> of all components of a specific type + std::vector> GetComponentsByType() const; private: static ComponentManager mInstance; //Singleton diff --git a/mwe/ecs-homemade/inc/ComponentManager.hpp b/mwe/ecs-homemade/inc/ComponentManager.hpp index a120ab1..92db1d4 100644 --- a/mwe/ecs-homemade/inc/ComponentManager.hpp +++ b/mwe/ecs-homemade/inc/ComponentManager.hpp @@ -1,123 +1,128 @@ template -T& ComponentManager::AddComponent(std::uint32_t id, Args &&... args) { - std::type_index type = typeid( - T); //Determine the type of T (this is used as the key of the unordered_map<>) - - if (mComponents.find(type) - == mComponents - .end()) { //Check if this component type is already in the unordered_map<> - mComponents[type] = std::vector>>(); //If not, create a new (empty) vector<> of vector> +T & ComponentManager::AddComponent(std::uint32_t id, Args &&... args) { + //Determine the type of T (this is used as the key of the unordered_map<>) + std::type_index type = typeid(T); + + //Check if this component type is already in the unordered_map<> + if (mComponents.find(type) == mComponents.end()) { + //If not, create a new (empty) vector<> of vector> + mComponents[type] + = std::vector>>(); } - if (id - >= mComponents[type] - .size()) { //Resize the vector<> if the id is greater than the current size - mComponents[type].resize( - id - + 1); //Initialize new slots to nullptr (resize does automatically init to nullptr) + //Resize the vector<> if the id is greater than the current size + if (id >= mComponents[type].size()) { + //Initialize new slots to nullptr (resize does automatically init to nullptr) + mComponents[type].resize(id + 1); } - mComponents[type][id].push_back(std::make_unique(std::forward( - args)...)); //Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> + //Create a new component of type T using perfect forwarding and store its unique_ptr in the vector<> + mComponents[type][id].push_back( + std::make_unique(std::forward(args)...)); - return static_cast(*mComponents[type][id].back().get()); + return static_cast(*mComponents[type][id].back().get()); } template void ComponentManager::DeleteComponentsById(std::uint32_t id) { - std::type_index type = typeid( - T); //Determine the type of T (this is used as the key of the unordered_map<>) + //Determine the type of T (this is used as the key of the unordered_map<>) + std::type_index type = typeid(T); - if (mComponents.find(type) - != mComponents.end()) { //Find the type (in the unordered_map<>) + //Find the type (in the unordered_map<>) + if (mComponents.find(type) != mComponents.end()) { + //Get the correct vector<> std::vector>> & componentArray - = mComponents[type]; //Get the correct vector<> + = mComponents[type]; - if (id - < componentArray - .size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> - componentArray[id] - .clear(); //Clear the whole vector<> of this specific type and id + //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + if (id < componentArray.size()) { + //Clear the whole vector<> of this specific type and id + componentArray[id].clear(); } } } template void ComponentManager::DeleteComponents() { - std::type_index type = typeid( - T); //Determine the type of T (this is used as the key of the unordered_map<>) + //Determine the type of T (this is used as the key of the unordered_map<>) + std::type_index type = typeid(T); - if (mComponents.find(type) - != mComponents.end()) { //Find the type (in the unordered_map<>) - mComponents[type] - .clear(); //Clear the whole vector<> of this specific type + //Find the type (in the unordered_map<>) + if (mComponents.find(type) != mComponents.end()) { + //Clear the whole vector<> of this specific type + mComponents[type].clear(); } } template std::vector> ComponentManager::GetComponentsByID(std::uint32_t id) const { - std::type_index type = typeid( - T); //Determine the type of T (this is used as the key of the unordered_map<>) + //Determine the type of T (this is used as the key of the unordered_map<>) + std::type_index type = typeid(T); - std::vector> - componentVector; //Create an empty vector<> + //Create an empty vector<> + std::vector> componentVector; - if (mComponents.find(type) - != mComponents.end()) { //Find the type (in the unordered_map<>) + //Find the type (in the unordered_map<>) + if (mComponents.find(type) != mComponents.end()) { + //Get the correct vector<> const std::vector>> & componentArray - = mComponents.at(type); //Get the correct vector<> + = mComponents.at(type); - if (id - < componentArray - .size()) { //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + //Make sure that the id (that we are looking for) is within the boundaries of the vector<> + if (id < componentArray.size()) { + //Loop trough the whole vector<> for (const std::unique_ptr & componentPtr : - componentArray[id]) { //Loop trough the whole vector<> - T * castedComponent = static_cast( - componentPtr.get()); //Cast the unique_ptr to a raw pointer - - if (castedComponent) { //Ensure that the cast was successful - componentVector.push_back( - *castedComponent); //Add the dereferenced raw pointer to the vector<> + componentArray[id]) { + //Cast the unique_ptr to a raw pointer + T * castedComponent = static_cast(componentPtr.get()); + + //Ensure that the cast was successful + if (castedComponent) { + //Add the dereferenced raw pointer to the vector<> + componentVector.push_back(*castedComponent); } } } } - return componentVector; //Return the vector<> + //Return the vector<> + return componentVector; } template std::vector> ComponentManager::GetComponentsByType() const { - std::type_index type = typeid( - T); //Determine the type of T (this is used as the key of the unordered_map<>) + //Determine the type of T (this is used as the key of the unordered_map<>) + std::type_index type = typeid(T); - std::vector> - componentVector; //Create an empty vector<> + //Create an empty vector<> + std::vector> componentVector; //std::uint32_t id = 0; //Set the id to 0 (the id will also be stored in the returned vector<>) - if (mComponents.find(type) - != mComponents.end()) { //Find the type (in the unordered_map<>) + //Find the type (in the unordered_map<>) + if (mComponents.find(type) != mComponents.end()) { + //Get the correct vector<> const std::vector>> & componentArray - = mComponents.at(type); //Get the correct vector<> + = mComponents.at(type); + //Loop through the whole vector<> for (const std::vector> & component : - componentArray) { //Loop through the whole vector<> - for (const std::unique_ptr & componentPtr : - component) { //Loop trough the whole vector<> - T * castedComponent = static_cast( - componentPtr.get()); //Cast the unique_ptr to a raw pointer - - if (castedComponent) { //Ensure that the cast was successful - componentVector.emplace_back(std::ref( - *castedComponent)); //Pair the dereferenced raw pointer and the id and add it to the vector<> + componentArray) { + //Loop trough the whole vector<> + for (const std::unique_ptr & componentPtr : component) { + //Cast the unique_ptr to a raw pointer + T * castedComponent = static_cast(componentPtr.get()); + + //Ensure that the cast was successful + if (castedComponent) { + //Pair the dereferenced raw pointer and the id and add it to the vector<> + componentVector.emplace_back(std::ref(*castedComponent)); } } @@ -125,5 +130,6 @@ ComponentManager::GetComponentsByType() const { } } - return componentVector; //Return the vector<> + //Return the vector<> + return componentVector; } diff --git a/mwe/ecs-homemade/inc/Components.h b/mwe/ecs-homemade/inc/Components.h index b392fd0..ad491e7 100644 --- a/mwe/ecs-homemade/inc/Components.h +++ b/mwe/ecs-homemade/inc/Components.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include class Component { public: @@ -40,7 +40,7 @@ public: virtual void onUpdate() = 0; }; -template +template class BehaviourWrapper : public IBehaviour { public: BehaviourWrapper(); @@ -53,7 +53,7 @@ private: class BehaviourScript : public Component { public: - template + template void addScript(); void onStart(); diff --git a/mwe/ecs-homemade/inc/Components.hpp b/mwe/ecs-homemade/inc/Components.hpp index 85da5a5..436a28c 100644 --- a/mwe/ecs-homemade/inc/Components.hpp +++ b/mwe/ecs-homemade/inc/Components.hpp @@ -1,20 +1,20 @@ #include "Components.h" #include -template -BehaviourWrapper::BehaviourWrapper() : instance() {} +template +BehaviourWrapper::BehaviourWrapper() : instance() {} -template +template void BehaviourWrapper::onStart() { instance.onStart(); } -template +template void BehaviourWrapper::onUpdate() { instance.onUpdate(); } -template +template void BehaviourScript::addScript() { behaviour = std::make_unique>(); } diff --git a/mwe/ecs-homemade/inc/GameObjectMax.h b/mwe/ecs-homemade/inc/GameObjectMax.h index c6af50a..5fab44a 100644 --- a/mwe/ecs-homemade/inc/GameObjectMax.h +++ b/mwe/ecs-homemade/inc/GameObjectMax.h @@ -8,7 +8,7 @@ public: GameObject(std::uint32_t id, std::string name, std::string tag, int layer); template - T& AddComponent(Args &&... args); + T & AddComponent(Args &&... args); std::uint32_t mId; std::string mName; diff --git a/mwe/ecs-homemade/inc/GameObjectMax.hpp b/mwe/ecs-homemade/inc/GameObjectMax.hpp index 4104589..2f433bb 100644 --- a/mwe/ecs-homemade/inc/GameObjectMax.hpp +++ b/mwe/ecs-homemade/inc/GameObjectMax.hpp @@ -1,7 +1,7 @@ #include "ComponentManager.h" template -T& GameObject::AddComponent(Args &&... args) { +T & GameObject::AddComponent(Args &&... args) { return ComponentManager::GetInstance().AddComponent( mId, std::forward(args)...); } diff --git a/mwe/ecs-homemade/src/Components.cpp b/mwe/ecs-homemade/src/Components.cpp index e6a4673..de8753e 100644 --- a/mwe/ecs-homemade/src/Components.cpp +++ b/mwe/ecs-homemade/src/Components.cpp @@ -11,13 +11,13 @@ Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) Colider::Colider(int size) : mSize(size) {} void BehaviourScript::onStart() { - if(behaviour) { + if (behaviour) { behaviour->onStart(); } } void BehaviourScript::onUpdate() { - if(behaviour) { + if (behaviour) { behaviour->onUpdate(); } } diff --git a/mwe/ecs-homemade/src/main.cpp b/mwe/ecs-homemade/src/main.cpp index e4a8bda..70c5d2c 100644 --- a/mwe/ecs-homemade/src/main.cpp +++ b/mwe/ecs-homemade/src/main.cpp @@ -9,13 +9,9 @@ class myScript { public: - void onStart() { - std::cout << "In onStart" << std::endl; - } + void onStart() { std::cout << "In onStart" << std::endl; } - void onUpdate() { - std::cout << "In onUpdate" << std::endl; - } + void onUpdate() { std::cout << "In onUpdate" << std::endl; } }; int main() { @@ -57,7 +53,8 @@ int main() { } std::vector> scripts - = ComponentManager::GetInstance().GetComponentsByType(); + = ComponentManager::GetInstance() + .GetComponentsByType(); for (BehaviourScript & script : scripts) { //script.onStart(); //script.onUpdate(); diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp index 084cd33..2ea0c70 100644 --- a/src/crepe/ComponentManager.hpp +++ b/src/crepe/ComponentManager.hpp @@ -10,7 +10,8 @@ template void ComponentManager::add_component(uint32_t id, Args &&... args) { using namespace std; - static_assert(is_base_of::value, "add_component must recieve a derivative class of Component"); + static_assert(is_base_of::value, + "add_component must recieve a derivative class of Component"); // Determine the type of T (this is used as the key of the unordered_map<>) type_index type = typeid(T); diff --git a/src/crepe/Script.cpp b/src/crepe/Script.cpp index 42e3666..3b83b7c 100644 --- a/src/crepe/Script.cpp +++ b/src/crepe/Script.cpp @@ -2,6 +2,5 @@ using namespace crepe; -void Script::init() { } -void Script::update() { } - +void Script::init() {} +void Script::update() {} diff --git a/src/crepe/Script.h b/src/crepe/Script.h index ba4073a..cdd6814 100644 --- a/src/crepe/Script.h +++ b/src/crepe/Script.h @@ -12,5 +12,4 @@ protected: // added event. }; -} - +} // namespace crepe diff --git a/src/crepe/ScriptSystem.cpp b/src/crepe/ScriptSystem.cpp index e301c71..988bb71 100644 --- a/src/crepe/ScriptSystem.cpp +++ b/src/crepe/ScriptSystem.cpp @@ -4,19 +4,12 @@ using namespace crepe; -ScriptSystem::ScriptSystem() { - dbg_trace(); -} -ScriptSystem::~ScriptSystem() { - dbg_trace(); -} +ScriptSystem::ScriptSystem() { dbg_trace(); } +ScriptSystem::~ScriptSystem() { dbg_trace(); } ScriptSystem & ScriptSystem::get_instance() { static ScriptSystem instance; return instance; } -void ScriptSystem::update() { - dbg_trace(); -} - +void ScriptSystem::update() { dbg_trace(); } diff --git a/src/crepe/ScriptSystem.h b/src/crepe/ScriptSystem.h index e1ed290..72e360b 100644 --- a/src/crepe/ScriptSystem.h +++ b/src/crepe/ScriptSystem.h @@ -14,5 +14,4 @@ private: ~ScriptSystem(); }; -} - +} // namespace crepe diff --git a/src/crepe/System.h b/src/crepe/System.h index 3fe3d66..8744920 100644 --- a/src/crepe/System.h +++ b/src/crepe/System.h @@ -8,8 +8,8 @@ public: virtual void update() = 0; protected: - System() { }; - virtual ~System() { }; + System(){}; + virtual ~System(){}; private: // singleton @@ -19,5 +19,4 @@ private: System & operator=(System &&) = delete; }; -} - +} // namespace crepe diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp index 2dd933e..84bfd4c 100644 --- a/src/crepe/api/BehaviorScript.cpp +++ b/src/crepe/api/BehaviorScript.cpp @@ -4,7 +4,4 @@ using namespace crepe::api; -BehaviorScript::BehaviorScript() { - dbg_trace(); -} - +BehaviorScript::BehaviorScript() { dbg_trace(); } diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index e9542c1..1d05a75 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -1,7 +1,7 @@ #pragma once -#include "../Script.h" #include "../Component.h" +#include "../Script.h" namespace crepe::api { @@ -13,5 +13,4 @@ protected: BehaviorScript(); }; -} - +} // namespace crepe::api diff --git a/src/example/script.cpp b/src/example/script.cpp index 28605c7..a610b83 100644 --- a/src/example/script.cpp +++ b/src/example/script.cpp @@ -3,10 +3,10 @@ * Standalone example for usage of the script component and system */ -#include -#include #include #include +#include +#include #include @@ -14,9 +14,7 @@ using namespace crepe; using namespace std; class MyScript : public api::BehaviorScript { - void update() { - dbg_trace(); - } + void update() { dbg_trace(); } }; int main() { @@ -30,4 +28,3 @@ int main() { return 0; } - -- cgit v1.2.3 From 1f82ffa4d3ee8355215d43bf43edf8cecaca0d1d Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Wed, 16 Oct 2024 17:21:04 +0200 Subject: fix user script implementation --- src/crepe/CMakeLists.txt | 1 - src/crepe/ComponentManager.h | 2 +- src/crepe/ComponentManager.hpp | 5 +++-- src/crepe/GameObject.h | 2 +- src/crepe/GameObject.hpp | 4 ++-- src/crepe/Script.cpp | 7 ------- src/crepe/Script.h | 16 ---------------- src/crepe/ScriptSystem.cpp | 27 ++++++++++++++++++++++----- src/crepe/ScriptSystem.h | 12 +++++++++++- src/crepe/api/BehaviorScript.h | 24 ++++++++++++++++++------ src/crepe/api/BehaviorScript.hpp | 18 ++++++++++++++++++ src/crepe/api/CMakeLists.txt | 2 ++ src/crepe/api/Script.cpp | 5 +++++ src/crepe/api/Script.h | 21 +++++++++++++++++++++ src/example/script.cpp | 7 ++++--- 15 files changed, 108 insertions(+), 45 deletions(-) delete mode 100644 src/crepe/Script.cpp delete mode 100644 src/crepe/Script.h create mode 100644 src/crepe/api/BehaviorScript.hpp create mode 100644 src/crepe/api/Script.cpp create mode 100644 src/crepe/api/Script.h diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index d85aef0..8323490 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -9,7 +9,6 @@ target_sources(crepe PUBLIC Rigidbody.cpp Sprite.cpp ScriptSystem.cpp - Script.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES diff --git a/src/crepe/ComponentManager.h b/src/crepe/ComponentManager.h index eab9b45..38f32e4 100644 --- a/src/crepe/ComponentManager.h +++ b/src/crepe/ComponentManager.h @@ -23,7 +23,7 @@ public: public: //! Add a component of a specific type template - void add_component(uint32_t id, Args &&... args); + T & add_component(uint32_t id, Args &&... args); //! Deletes all components of a specific type and id template void delete_components_by_id(uint32_t id); diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp index c872594..e0242a2 100644 --- a/src/crepe/ComponentManager.hpp +++ b/src/crepe/ComponentManager.hpp @@ -3,12 +3,11 @@ #include #include "ComponentManager.h" -#include "api/BehaviorScript.h" namespace crepe { template -void ComponentManager::add_component(uint32_t id, Args &&... args) { +T & ComponentManager::add_component(uint32_t id, Args &&... args) { using namespace std; static_assert(is_base_of::value, "add_component must recieve a derivative class of Component"); @@ -33,6 +32,8 @@ void ComponentManager::add_component(uint32_t id, Args &&... args) { T * instance = new T(forward(args)...); // store its unique_ptr in the vector<> components[type][id].push_back(unique_ptr(instance)); + + return *instance; } template diff --git a/src/crepe/GameObject.h b/src/crepe/GameObject.h index 3588d9a..b5d6399 100644 --- a/src/crepe/GameObject.h +++ b/src/crepe/GameObject.h @@ -10,7 +10,7 @@ public: GameObject(uint32_t id, std::string name, std::string tag, int layer); template - void add_component(Args &&... args); + T & add_component(Args &&... args); uint32_t id; std::string name; diff --git a/src/crepe/GameObject.hpp b/src/crepe/GameObject.hpp index 5966fbf..8cd1abe 100644 --- a/src/crepe/GameObject.hpp +++ b/src/crepe/GameObject.hpp @@ -7,9 +7,9 @@ namespace crepe { template -void GameObject::add_component(Args &&... args) { +T & GameObject::add_component(Args &&... args) { auto & mgr = ComponentManager::get_instance(); - mgr.add_component(id, std::forward(args)...); + return mgr.add_component(id, std::forward(args)...); } } // namespace crepe diff --git a/src/crepe/Script.cpp b/src/crepe/Script.cpp deleted file mode 100644 index 42e3666..0000000 --- a/src/crepe/Script.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "Script.h" - -using namespace crepe; - -void Script::init() { } -void Script::update() { } - diff --git a/src/crepe/Script.h b/src/crepe/Script.h deleted file mode 100644 index ba4073a..0000000 --- a/src/crepe/Script.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -namespace crepe { - -class Script { -protected: - virtual void init(); - virtual void update(); - // NOTE: additional *events* (like unity's OnDisable and OnEnable) should be - // implemented as member methods in derivative user script classes and - // registered in init(), otherwise this class will balloon in size with each - // added event. -}; - -} - diff --git a/src/crepe/ScriptSystem.cpp b/src/crepe/ScriptSystem.cpp index e40909e..0537c16 100644 --- a/src/crepe/ScriptSystem.cpp +++ b/src/crepe/ScriptSystem.cpp @@ -1,12 +1,16 @@ +#include +#include #include + +#include "ScriptSystem.h" #include "ComponentManager.h" #include "api/BehaviorScript.h" -#include "util/fmt.h" +#include "api/Script.h" #include "util/log.h" -#include "ScriptSystem.h" - +using namespace std; using namespace crepe; +using namespace crepe::api; ScriptSystem::ScriptSystem() { dbg_trace(); @@ -24,10 +28,23 @@ void ScriptSystem::update() { using namespace std; dbg_trace(); + forward_list