diff --git a/.github/workflows/build_ubuntu.yml b/.github/workflows/build_ubuntu.yml index 7895429db5..d626103b94 100644 --- a/.github/workflows/build_ubuntu.yml +++ b/.github/workflows/build_ubuntu.yml @@ -6,13 +6,13 @@ on: - main push: branches: - - main + - "*" jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true @@ -23,17 +23,18 @@ jobs: sudo apt-get install libefl-all-dev sudo apt-get install python3-pip sudo apt-get install libturbojpeg0-dev libpng-dev libwebp-dev + sudo apt-get install libglew-dev libglfw3-dev sudo pip3 install meson - name: Build run: | - meson setup build -Dlog=true -Dexamples=true -Dloaders="all" -Dsavers="all" -Dbindings="capi" -Dtools="all" + meson setup build -Dlog=true -Dexamples=true -Dloaders="all" -Dsavers="all" -Dbindings="capi" -Dtools="all" -Dwindows=true sudo ninja -C build install static_loaders: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true @@ -41,18 +42,30 @@ jobs: run: | sudo apt-get update sudo apt-get install ninja-build gcc-multilib g++-multilib + sudo apt-get install libglew-dev libglfw3-dev sudo apt-get install python3-pip sudo pip3 install meson - name: Build run: | - meson setup build -Dlog=true -Dloaders="all" -Dsavers="all" -Dbindings="capi" -Dtools="all" -Dstatic=true + meson setup --prefix=`pwd`/static --default-library static -Dlog=true -Dbuildtype=debug -Dstrip=false -Dloaders="all" -Dsavers="all" -Dbindings="capi" -Dtools="all" -Dstatic=true -Dwindows=true build . sudo ninja -C build install + - name: Tar_files + run: | + mkdir ${{github.workspace}}/tar + ls -all + cd ${{github.workspace}}/static/ && tar -czvf ${{github.workspace}}/tar/thorvg_linux64_static.tgz * + + - uses: actions/upload-artifact@v4 + with: + name: horvg_linux64_static + path: ${{github.workspace}}/tar + unit_test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true @@ -64,6 +77,7 @@ jobs: sudo apt-get install software-properties-common sudo apt-get install python3-pip sudo apt-get install libturbojpeg0-dev libpng-dev libwebp-dev + sudo apt-get install libglew-dev libglfw3-dev sudo pip3 install meson - name: Build @@ -71,7 +85,7 @@ jobs: meson setup build -Dloaders="all" -Dsavers="all" -Dbindings="capi" -Dtests=true --errorlogs sudo ninja -C build install test - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: UnitTestReport path: build/meson-logs/testlog.txt diff --git a/inc/meson.build b/inc/meson.build index e9d002dd51..e8cdd527f3 100644 --- a/inc/meson.build +++ b/inc/meson.build @@ -1,3 +1,7 @@ header_files = ['thorvg.h'] +if get_option('windows') == true + header_files += ['thorvg_window.h'] +endif + install_headers(header_files) diff --git a/inc/thorvg_window.h b/inc/thorvg_window.h new file mode 100644 index 0000000000..ee9a883928 --- /dev/null +++ b/inc/thorvg_window.h @@ -0,0 +1,61 @@ +/*! + * @file thorvg_window.h + * + */ + + +#ifndef _THORVG_WINDOW_H_ +#define _THORVG_WINDOW_H_ + +#include +#include +#include +#include +#include + +namespace tvg +{ + +/** + * @class Window + * + * @brief . + * + * + * @BETA_API + */ +class TVG_API Window final +{ +public: + ~Window(); + + void close(); + + bool run(); + + void resize(int width, int height); + + void init(std::function on_init); + + void update(std::function on_update); + + /** + * @brief Creates a new Window object. + * + * @return A new Window object. + */ + static std::unique_ptr gen(int width = 1, int height = 1, std::string name = "dummy", tvg::CanvasEngine engine = tvg::CanvasEngine::Sw) noexcept; + + static void loop(); +private: + // hmmm ....? + Window(int width, int height, std::string name, tvg::CanvasEngine engine); + _TVG_DECLARE_PRIVATE(Window); +}; + + +/** @}*/ + +} //namespace + +#endif //_THORVG_WINDOW_H_ diff --git a/meson.build b/meson.build index 9dece91d6b..48519a6f77 100644 --- a/meson.build +++ b/meson.build @@ -159,6 +159,7 @@ Summary: Tool (Svg2Tvg): @22@ Tool (Svg2Png): @23@ Tool (Lottie2Gif): @24@ + tvg::Window: @25@ '''.format( meson.project_version(), @@ -186,6 +187,7 @@ Summary: all_tools or get_option('tools').contains('svg2tvg'), all_tools or get_option('tools').contains('svg2png'), all_tools or get_option('tools').contains('lottie2gif'), + get_option('windows'), ) message(summary) diff --git a/meson_options.txt b/meson_options.txt index c9e6c89af6..e13745b39e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -57,3 +57,8 @@ option('static', type: 'boolean', value: false, description: 'Force to use static linking modules in thorvg') + +option('windows', + type: 'boolean', + value: false, + description: 'Enable using tvg::Window based on GLFW') diff --git a/src/examples/Window.cpp b/src/examples/Window.cpp new file mode 100644 index 0000000000..61ba8cda94 --- /dev/null +++ b/src/examples/Window.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2023 the ThorVG project. All rights reserved. + + * 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. + */ + +#include +#include +#include + +#include + +using namespace std; + +static uint32_t WIDTH = 800; +static uint32_t HEIGHT = 800; + +static unique_ptr window; +static unique_ptr window2; +static unique_ptr window3; +static unique_ptr window4; + +/************************************************************************/ +/* Main Code */ +/************************************************************************/ + +int main(int argc, char **argv) +{ + //Threads Count + auto threads = std::thread::hardware_concurrency(); + if (threads > 0) --threads; //Allow the designated main thread capacity + + //Initialize ThorVG Engine + if (tvg::Initializer::init(threads) == tvg::Result::Success) { + + window = tvg::Window::gen(WIDTH, HEIGHT, "GLFW Window Example 1 (Sw)", tvg::CanvasEngine::Sw); + + window->init([](tvg::Canvas* canvas) { + auto main_scene = tvg::Scene::gen(); + auto shape1 = tvg::Shape::gen(); + shape1->appendRect(0, 0, 200, 200); //x, y, w, h + shape1->appendRect(100, 100, 300, 300, 100, 100); //x, y, w, h, rx, ry + shape1->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH + shape1->appendCircle(400, 500, 170, 100); //cx, cy, radiusW, radiusH + shape1->fill(255, 0, 0); //r, g, b + main_scene->push(std::move(shape1)); + auto picture = tvg::Picture::gen(); + if (picture->load(EXAMPLE_DIR"/cartman.svg") != tvg::Result::Success) { + return false; + } + picture->translate(150, 150); + picture->size(100, 100); + main_scene->push(std::move(picture)); + canvas->push(std::move(main_scene)); +printf("inited 1\n"); + return true; + }); +/* + window->update([](tvg::Canvas* canvas) { + canvas->update(); +printf("updated 1\n"); + return true; + }); +*/ + window2 = tvg::Window::gen(WIDTH, HEIGHT, "GLFW Window Example 2 (Gl)", tvg::CanvasEngine::Gl); + + window2->init([](tvg::Canvas* canvas) { + auto main_scene = tvg::Scene::gen(); + auto shape1 = tvg::Shape::gen(); + shape1->appendRect(0, 0, 200, 200); //x, y, w, h + shape1->appendRect(100, 100, 300, 300, 100, 100); //x, y, w, h, rx, ry + shape1->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH + shape1->appendCircle(400, 500, 170, 100); //cx, cy, radiusW, radiusH + shape1->fill(0, 255, 0); //r, g, b + main_scene->push(std::move(shape1)); + auto picture = tvg::Picture::gen(); + if (picture->load(EXAMPLE_DIR"/tiger.svg") != tvg::Result::Success) { + return false; + } + picture->translate(150, 150); + picture->size(100, 100); + main_scene->push(std::move(picture)); + canvas->push(std::move(main_scene)); +printf("inited 2\n"); + return true; + }); +/* + window2->update([](tvg::Canvas* canvas) { + canvas->update(); +printf("updated 2\n"); + return true; + }); + +*/ + window3 = tvg::Window::gen(WIDTH, HEIGHT, "GLFW Window Example 3 (Gl)", tvg::CanvasEngine::Gl); + + window3->init([](tvg::Canvas* canvas) { + auto main_scene = tvg::Scene::gen(); + auto shape1 = tvg::Shape::gen(); + shape1->appendRect(0, 0, 200, 200); //x, y, w, h + shape1->appendRect(100, 100, 300, 300, 100, 100); //x, y, w, h, rx, ry + shape1->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH + shape1->appendCircle(400, 500, 170, 100); //cx, cy, radiusW, radiusH + shape1->fill(0, 255, 0); //r, g, b + main_scene->push(std::move(shape1)); + + auto picture = tvg::Picture::gen(); + if (picture->load(EXAMPLE_DIR"/logo.svg") != tvg::Result::Success) { + return false; + } + picture->translate(150, 150); + picture->size(400, 400); + main_scene->push(std::move(picture)); + canvas->push(std::move(main_scene)); +printf("inited 3\n"); + return true; + }); + /* window3->update([](tvg::Canvas* canvas) { + canvas->update(); +printf("updated 3\n"); + return true; + }); +*/ + window4 = tvg::Window::gen(WIDTH, HEIGHT, "GLFW Window Example 4 (Sw)", tvg::CanvasEngine::Sw); + + auto ani = tvg::Animation::gen(); + auto animation = ani.get();//tvg::Animation::gen(); + window4->init([=](tvg::Canvas* canvas) { + auto main_scene = tvg::Scene::gen(); + auto shape1 = tvg::Shape::gen(); + shape1->appendRect(0, 0, 200, 200); //x, y, w, h + shape1->appendRect(100, 100, 300, 300, 100, 100); //x, y, w, h, rx, ry + shape1->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH + shape1->appendCircle(400, 500, 170, 100); //cx, cy, radiusW, radiusH + shape1->fill(0, 255, 0); //r, g, b + main_scene->push(std::move(shape1)); + + auto picture = animation->picture(); + if (picture->load(EXAMPLE_DIR"/alien.json") != tvg::Result::Success) { + return false; + } + picture->translate(150, 150); + picture->size(400, 400); + canvas->push(std::move(main_scene)); + canvas->push(tvg::cast(animation->picture())); +printf("inited 4\n"); + return true; + }); + + window4->update([=](tvg::Canvas* canvas) { + uint32_t cur = animation->curFrame(); + cur = (cur + 1) % (uint32_t)animation->totalFrame(); + animation->frame(cur); + canvas->update(animation->picture()); +//printf("updated 4\n"); + return true; + }); + + tvg::Window::loop(); + + tvg::Initializer::term(); + + } else { + cout << "engine is not supported" << endl; + } + return 0; +} diff --git a/src/examples/meson.build b/src/examples/meson.build index 6ace2f307a..fc7149e1e0 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -59,6 +59,12 @@ source_file = [ 'Update.cpp', ] +if get_option('windows') == true + source_file += [ + 'Window.cpp', + ] +endif + foreach current_file : source_file name = current_file.split('.')[0] executable(name, current_file, diff --git a/src/meson.build b/src/meson.build index 1385a7efd0..812001c0d7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -48,6 +48,13 @@ if get_option('threads') == true and host_machine.system() != 'windows' and host thorvg_lib_dep += [thread_dep] endif +if get_option('windows') == true + subdir('windows') + if windows_dep.found() + thorvg_lib_dep += windows_dep + endif +endif + subdir('bindings') thorvg_lib = library( diff --git a/src/windows/meson.build b/src/windows/meson.build new file mode 100644 index 0000000000..1237a40194 --- /dev/null +++ b/src/windows/meson.build @@ -0,0 +1,18 @@ +engine_dep = [] + +source_file = [ + 'tvgWindowImpl.h', + 'tvgWindow.cpp', +] + +glew_dep = dependency('glew', required: true) +glfw3_dep = dependency('glfw3', required: true) + +engine_dep += glew_dep +engine_dep += glfw3_dep + +windows_dep = declare_dependency( + dependencies : engine_dep, + include_directories : include_directories('.'), + sources : source_file +) diff --git a/src/windows/tvgWindow.cpp b/src/windows/tvgWindow.cpp new file mode 100644 index 0000000000..9410050041 --- /dev/null +++ b/src/windows/tvgWindow.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 the ThorVG project. All rights reserved. + + * 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. + */ + +#include "tvgWindowImpl.h" + +#include +#include + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +Window::Window(int width, int height, std::string name, tvg::CanvasEngine engine) + : pImpl(new Impl(this, width, height, name, engine)) +{ + // Window::pImpl->id = TVG_CLASS_ID_WINDOW; + printf("Called Constructor (%d %d) %s\n", width, height, name.c_str()); +} + + +Window::~Window() +{ + printf("Called Destructor %p\n",pImpl->gl_window); + delete(pImpl); +} + + +unique_ptr Window::gen(int width, int height, std::string name, tvg::CanvasEngine engine) noexcept +{ + return unique_ptr(new Window(width, height, name, engine)); +} + + +void Window::close() +{ + pImpl->close(); +} + +void Window::loop() +{ + while (true) { + bool exit = true; + for (auto window : window_list) { + if (window->run()) { + exit = false; + } else { + window->close(); + window_list.erase(std::remove(window_list.begin(), window_list.end(), window), window_list.end()); + } + } + if (exit) + break; + } +} + +bool Window::run() +{ + return pImpl->run(); +} + + +void Window::resize(int width, int height) +{ + pImpl->resize(width, height); +} + +void Window::init(std::function on_init) +{ + pImpl->init(on_init); +} + +void Window::update(std::function on_update) +{ + pImpl->update(on_update); +} diff --git a/src/windows/tvgWindowImpl.h b/src/windows/tvgWindowImpl.h new file mode 100644 index 0000000000..af5173c145 --- /dev/null +++ b/src/windows/tvgWindowImpl.h @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2023 the ThorVG project. All rights reserved. + + * 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. + */ + + +#ifndef _TVG_WINDOW_IMPL_H_ +#define _TVG_WINDOW_IMPL_H_ + +#include +#include +#include +#include + +// sudo apt install libglew-dev -y +// sudo apt install libglfw3-dev libglfw3 -y +#include +#include +//#define GLFW_DLL + +#include "tvgCommon.h" +#include "thorvg_window.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +const int MAX_WINDOW = 10; +static std::vector window_list; +static constexpr auto BPP = 4; + +struct Window::Impl +{ + std::string glsl_version; + GLFWwindow* gl_window = nullptr; + uint32_t* buffer = nullptr; + GLuint texture = 0; + int width = 0; + int height = 0; + std::unique_ptr canvas = nullptr; + double lastTime = 0; + double fps = 0; + bool shoudClose = false; + std::function on_update; + tvg::CanvasEngine engine = tvg::CanvasEngine::Sw; + + static void glfwOnFramebufferResize(GLFWwindow* gl_window, int w, int h) { + for (auto window : window_list) { + if(window->pImpl->gl_window == gl_window) { + window->resize(w,h); + break; + } + } + } + + Impl(tvg::Window* window, int w, int h, std::string name, tvg::CanvasEngine engine_) + { + window_list.push_back(window); + engine = engine_; + + if (!glfwInit()) { + printf("Fail to glfwInit()\n"); + return; + } + + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwWindowHint(GLFW_RED_BITS, mode->redBits); + glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); + glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); + glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); + glfwWindowHint(GLFW_STENCIL_BITS, 0); + glfwWindowHint(GLFW_DEPTH_BITS, 0); + + // Decide GL+GLSL versions + #if defined(__APPLE__) + // GL 3.2 + GLSL 150 + glsl_version = "#version 150"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac + #else + // GL 3.0 + GLSL 130 + glsl_version = "#version 130"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only + #endif + + gl_window = glfwCreateWindow(w, h, name.c_str(), NULL, NULL); + if (!gl_window) { + glfwTerminate(); + printf("Fail to glfwCreateWindow()\n"); + return; + } + + glfwSetFramebufferSizeCallback(gl_window, Window::Impl::glfwOnFramebufferResize); + + glfwMakeContextCurrent(gl_window); + //glfwSwapInterval(1); + glEnable(GL_TEXTURE_2D); + if (engine == tvg::CanvasEngine::Sw) { + canvas = tvg::SwCanvas::gen(); + } + else if (engine == tvg::CanvasEngine::Gl) { + canvas = tvg::GlCanvas::gen(); + } + + glfwGetWindowSize(gl_window, &width, &height); + resize(width, height); + + lastTime = glfwGetTime(); + + printf("Called WindowImpl Constructor\n"); + } + + ~Impl() + { + printf("Called WindowImpl Destructor\n"); + } + + void close() + { + printf("Called WindowImpl close()\n"); + if (buffer) free(buffer); + } + + bool run() + { + glfwMakeContextCurrent(gl_window); + if (glfwWindowShouldClose(gl_window) || + glfwGetKey(gl_window, GLFW_KEY_ESCAPE)) + { + glfwDestroyWindow (gl_window); + return false; + } + + // User render loop + double thisTime = glfwGetTime(); + fps = 1.0 / (thisTime - lastTime); + + //sw_canvas->clear(); + bool updated = false; + if (on_update) updated = on_update(canvas.get()); + if (updated) { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + // Note: Gl renderer should to call target() again. + if (engine == tvg::CanvasEngine::Gl) { + auto gl_canvas = reinterpret_cast(canvas.get()); + gl_canvas->target(nullptr, width * BPP, width, height); + } + if (canvas->draw() == tvg::Result::Success) canvas->sync(); + } + + // Render the buffer to a texture and display it + glBindTexture(GL_TEXTURE_2D, texture); + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, 0, + width, height, + GL_RGBA, + GL_UNSIGNED_BYTE, + (void*)buffer + ); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(0, 0); + glTexCoord2f(1, 0); glVertex2f(width, 0); + glTexCoord2f(1, 1); glVertex2f(width, height); + glTexCoord2f(0, 1); glVertex2f(0, height); + glEnd(); + glBindTexture(GL_TEXTURE_2D, 0); + + // Swap framebuffers + glfwSwapBuffers(gl_window); + glfwPollEvents(); + lastTime = thisTime; + + + return true; + } + + void resize(int w, int h) + { + // Create a new framebuffer + width = w; + height = h; + buffer = static_cast(realloc(buffer, width * height * sizeof(uint32_t))); + + // Create a new texture + glDeleteTextures(1, &texture); + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width, height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + (void*)buffer + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + // reattach buffer to tvg canvas + if (engine == tvg::CanvasEngine::Sw) { + auto sw_canvas = reinterpret_cast(canvas.get()); + sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ABGR8888); + } + else if (engine == tvg::CanvasEngine::Gl) { + auto gl_canvas = reinterpret_cast(canvas.get()); + static constexpr auto BPP = 4; + gl_canvas->target(nullptr, width * BPP, width, height); + } + // Set projection + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, width, height, 0.0f, 0.0f, 1.0f); + glViewport(0, 0, width, height); + if (canvas->draw() == tvg::Result::Success) canvas->sync(); + } + + void init(std::function on_init) + { + on_init(canvas.get()); + if (canvas->draw() == tvg::Result::Success) canvas->sync(); + } + + void update(std::function _on_update) + { + on_update = _on_update; + } +}; +#endif //_TVG_WINDOW_IMPL_H_