├── meson_options.txt ├── .gitignore ├── examples ├── Roboto-Regular.ttf ├── meson.build ├── CMakeLists.txt ├── basic-ny-gl.cpp ├── basic-ny.cpp ├── nanovg_gl_utils.h ├── offscreen.cpp ├── basic-windows.cpp ├── nanovg.h └── stb_image_write.h ├── subprojects ├── ny.wrap ├── vkpp.wrap ├── dlg.wrap ├── nytl.wrap └── vpp.wrap ├── .editorconfig ├── meson.build ├── src ├── shader │ ├── fill.vert │ ├── fill.vert.h │ ├── CMakeLists.txt │ ├── fill.frag │ └── fill.frag.h ├── CMakeLists.txt ├── nanovg_vk.h ├── vvg.hpp ├── nanovg.h └── renderer.cpp ├── docs ├── dynamic2.pseudo.cpp └── dynamic.pseudo.cpp ├── LICENSE.md └── README.md /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('examples', type: 'boolean', value : false) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | bin/ 3 | subprojects/*/ 4 | obj/ 5 | *.swp 6 | *.swo 7 | -------------------------------------------------------------------------------- /examples/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nyorain/vvg/HEAD/examples/Roboto-Regular.ttf -------------------------------------------------------------------------------- /subprojects/ny.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=ny 3 | url=https://github.com/nyorain/ny.git 4 | revision=master 5 | -------------------------------------------------------------------------------- /subprojects/vkpp.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=vkpp 3 | url=https://github.com/nyorain/vkpp.git 4 | revision=v0.1 -------------------------------------------------------------------------------- /subprojects/dlg.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=dlg 3 | url=https://github.com/nyorain/dlg.git 4 | revision=v0.2 5 | -------------------------------------------------------------------------------- /subprojects/nytl.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=nytl 3 | url=https://github.com/nyorain/nytl.git 4 | revision=v0.5 5 | -------------------------------------------------------------------------------- /subprojects/vpp.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=vpp 3 | url=https://github.com/nyorain/vpp.git 4 | revision=v0.2 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = tab 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /examples/meson.build: -------------------------------------------------------------------------------- 1 | dep_ny = dependency('ny', fallback: ['ny', 'ny_dep']) 2 | executable('basic-ny', 3 | sources: 'basic-ny.cpp', 4 | dependencies: [dep_ny, dep_vvg]) 5 | 6 | dep_gl = dependency('gl') 7 | executable('basic-ny-gl', 8 | sources: ['basic-ny-gl.cpp', '../src/nanovg.c'], 9 | dependencies: [dep_ny, dep_gl]) 10 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('vvg', ['cpp', 'c'], 2 | version: '0.1.0', 3 | meson_version: '>=0.38', 4 | default_options: ['cpp_std=c++1z']) 5 | 6 | add_project_arguments( 7 | '-Wall', 8 | '-Wextra', 9 | '-Wno-unused-parameter', 10 | '-Wpedantic', 11 | '-Wno-missing-braces', 12 | language: 'cpp') 13 | 14 | dep_vpp = dependency('vpp', fallback: ['vpp', 'vpp_dep']) 15 | 16 | vvg = library('vvg', 17 | sources: ['src/renderer.cpp', 'src/nanovg.c'], 18 | dependencies: dep_vpp) 19 | 20 | dep_vvg = declare_dependency( 21 | link_with: vvg, 22 | include_directories: include_directories('src'), 23 | dependencies: dep_vpp) 24 | 25 | examples = get_option('examples') 26 | if examples 27 | subdir('examples') 28 | endif 29 | -------------------------------------------------------------------------------- /src/shader/fill.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_ARB_separate_shader_objects : enable 4 | #extension GL_ARB_shading_language_420pack : enable 5 | 6 | layout(location = 0) in vec2 ivertex; 7 | layout(location = 1) in vec2 itexcoord; 8 | 9 | layout(location = 0) out vec2 opos; 10 | layout(location = 1) out vec2 otexcoord; 11 | 12 | layout(set = 0, binding = 0) uniform UBO 13 | { 14 | vec2 viewSize; 15 | } ubo; 16 | 17 | void main() 18 | { 19 | //just perform interpolation for texture coords and screen position 20 | otexcoord = itexcoord; 21 | opos = ivertex; 22 | 23 | //normalize the vertex coords from ([0, width], [0, height]) to ([-1, 1], [-1, 1]). 24 | //unlike in opengl there is no y inversion needed. 25 | gl_Position = vec4(2.0 * ivertex / ubo.viewSize - 1.0, 0.0, 1.0); 26 | } 27 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #vkg 2 | if(Shared) 3 | add_library(vvg SHARED renderer.cpp nanovg.c) 4 | else() 5 | add_library(vvg renderer.cpp nanovg.c) 6 | endif() 7 | 8 | if(Depend) 9 | add_dependencies(vvg vpp_ep) 10 | endif() 11 | 12 | # link to vulkan and vpp 13 | find_package(Vulkan REQUIRED) 14 | target_link_libraries(vvg vpp ${Vulkan_LIBRARY}) 15 | target_include_directories(vvg PUBLIC ${Vulkan_INCLUDE_DIR}) 16 | 17 | # copy runtime files 18 | add_custom_command( 19 | TARGET vvg PRE_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy_directory 21 | "${CMAKE_BINARY_DIR}/external/install/bin/." 22 | "${CMAKE_BINARY_DIR}/bin" 23 | COMMENT "Copying vpp bin files") 24 | 25 | #install 26 | install(TARGETS vvg 27 | RUNTIME DESTINATION bin 28 | LIBRARY DESTINATION lib 29 | ARCHIVE DESTINATION lib) 30 | -------------------------------------------------------------------------------- /docs/dynamic2.pseudo.cpp: -------------------------------------------------------------------------------- 1 | /// The data specifying how something should be drawn. 2 | /// Basically the render state, in nanovg terms. 3 | class RenderData { 4 | public: 5 | - transform 6 | - scissor 7 | - color (or gradient or texture) 8 | }; 9 | 10 | 11 | class RenderPath { 12 | public: 13 | - points and stuff 14 | }; 15 | 16 | class RenderOperation { 17 | public: 18 | - RenderData 19 | - RenderPath 20 | - stroke/fill/text 21 | }; 22 | 23 | 24 | // Degress of freedom: 25 | // - which buffer memory type to use (often updated?) 26 | // - which things should be changeable at runtime 27 | // - e.g. if it is alwasys a rectangle use vkCmdDraw, otherwise vkCmdDrawIndirect 28 | 29 | 30 | class MyRenderer { 31 | public: 32 | void init() { 33 | op_ = RenderOperatoin::rect({0, 0}, {10, 10}, Color::red); 34 | } 35 | 36 | void update() { 37 | if(condition) { 38 | // some examples 39 | } 40 | } 41 | 42 | void record(vk::CommandBuffer cmdBuf) { 43 | op_.record(cmdBuf); 44 | } 45 | 46 | RenderOperation op_; 47 | }; 48 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(COPY "Roboto-Regular.ttf" DESTINATION ${CMAKE_BINARY_DIR}) 2 | file(COPY "Roboto-Regular.ttf" DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 3 | 4 | # add_executable(basic basic-windows.cpp) 5 | # target_link_libraries(basic vpp vvg) 6 | 7 | add_executable(offscreen offscreen.cpp) 8 | target_link_libraries(offscreen vpp vvg) 9 | 10 | 11 | # example using ny 12 | ExternalProject_Add(ny_ep 13 | PREFIX ${CMAKE_BINARY_DIR}/ny 14 | GIT_REPOSITORY https://github.com/nyorain/ny.git 15 | GIT_TAG master 16 | INSTALL_DIR ${CMAKE_BINARY_DIR}/external/install 17 | CMAKE_ARGS ${ExternalProjectCMakeArgs}) 18 | 19 | ExternalProject_Add_Step(ny_ep 20 | forceinstall 21 | DEPENDEES configure 22 | DEPENDERS install 23 | ALWAYS 1) 24 | 25 | add_executable(basic-ny basic-ny.cpp) 26 | target_link_libraries(basic-ny vpp vvg ny) 27 | 28 | if(Depend) 29 | add_dependency(basic-ny ny_ep) 30 | endif() 31 | 32 | add_custom_command( 33 | TARGET basic-ny POST_BUILD 34 | COMMAND ${CMAKE_COMMAND} -E copy_directory 35 | "${CMAKE_BINARY_DIR}/external/install/bin/." 36 | "${CMAKE_BINARY_DIR}/bin" 37 | COMMENT "Copying external binary files") 38 | -------------------------------------------------------------------------------- /docs/dynamic.pseudo.cpp: -------------------------------------------------------------------------------- 1 | // TODO: add possibility to defer updates 2 | 3 | template 4 | class Parameter { 5 | public: 6 | Parameter() = defualt; 7 | Parameter(const T& value) : value_(value) {} 8 | Parameter(T&& value) : value_(std::move(value)) {} 9 | virtual ~Parameter() = default; 10 | 11 | virtual bool dynamic() const noexcept { return false; } 12 | virtual const T& get() const noexcept { return value_; } 13 | 14 | protected: 15 | T value_; 16 | }; 17 | 18 | template 19 | class DynamicParameter : public Parameter { 20 | public: 21 | using Parameter::Parameter; 22 | 23 | void set(const T& value) { value_ = value; updated(); } 24 | void set(T&& value) { value_ = value; updated(); } 25 | void updated() { if(observer_) observer_->changed(*this, value_); } 26 | 27 | ParameterObserver* observer() const { return observer_; } 28 | void observer(ParameterObserver* obs) const { return observer_ = obs; } 29 | 30 | virtual bool dynamic() const noexcept { return true; } 31 | 32 | protected: 33 | ParameterObserver* observer_ {}; 34 | }; 35 | 36 | template 37 | class ParameterObserver { 38 | virtual void change(DynamicParameter& obj, const T& value); 39 | }; 40 | 41 | class Renderer { 42 | public: 43 | void rect(const Parameter& pos, const Parameter& size); 44 | }; 45 | 46 | 47 | 48 | 49 | // static drawing 50 | renderer.rect({0, 0}, {100, 100}); 51 | renderer.fill(); 52 | renderer.record(); 53 | 54 | every frame { 55 | renderer.draw(); 56 | } 57 | 58 | 59 | // dynamic drawing 60 | DyanmicParameter position; 61 | DyanmicParameter size; 62 | 63 | renderer.rect(position, size); 64 | renderer.fill(); 65 | renderer.record(); 66 | 67 | every frame { 68 | updateRectAnimation(position, size); 69 | renderer.draw(); 70 | } 71 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## vvg and vpp 2 | 3 | Copyright © 2016 nyorain 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the “Software”), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | ## nanovg 28 | __This library redistributes, contains and uses code fragments of nanovg.__ 29 | nanovg is licensed under the following license: 30 | 31 | Copyright (c) 2013 Mikko Mononen memon@inside.org 32 | 33 | This software is provided 'as-is', without any express or implied 34 | warranty. In no event will the authors be held liable for any damages 35 | arising from the use of this software. 36 | 37 | Permission is granted to anyone to use this software for any purpose, 38 | including commercial applications, and to alter it and redistribute it 39 | freely, subject to the following restrictions: 40 | 41 | 1. The origin of this software must not be misrepresented; you must not 42 | claim that you wrote the original software. If you use this software 43 | in a product, an acknowledgment in the product documentation would be 44 | appreciated but is not required. 45 | 2. Altered source versions must be plainly marked as such, and must not be 46 | misrepresented as being the original software. 47 | 3. This notice may not be removed or altered from any source distribution. 48 | -------------------------------------------------------------------------------- /src/shader/fill.vert.h: -------------------------------------------------------------------------------- 1 | #ifndef BINARY_FILL_VERT_DATA_HEADER_INCLUDE 2 | #define BINARY_FILL_VERT_DATA_HEADER_INCLUDE 3 | 4 | #include 5 | 6 | //just check for c++11 7 | #if defined(__cplusplus) && __cplusplus >= 201103L 8 | constexpr 9 | #else 10 | const 11 | #endif 12 | 13 | uint32_t fill_vert_data[] = { 14 | 119734787, 65536, 524289, 44, 0, 131089, 1, 393227, 1, 1280527431, 1685353262, 15 | 808793134, 0, 196622, 0, 1, 655375, 0, 4, 1852399981, 0, 9, 11, 13, 14, 22, 16 | 196611, 2, 450, 589828, 1096764487, 1935622738, 1918988389, 1600484449, 17 | 1684105331, 1868526181, 1667590754, 29556, 589828, 1096764487, 1935622738, 18 | 1768186216, 1818191726, 1969712737, 1600481121, 1882206772, 7037793, 262149, 4, 19 | 1852399981, 0, 327685, 9, 2019914863, 1919905635, 100, 327685, 11, 2019914857, 20 | 1919905635, 100, 262149, 13, 1936683119, 0, 262149, 14, 1919252073, 7890292, 21 | 393221, 20, 1348430951, 1700164197, 2019914866, 0, 393222, 20, 0, 1348430951, 22 | 1953067887, 7237481, 458758, 20, 1, 1348430951, 1953393007, 1702521171, 0, 23 | 458758, 20, 2, 1130327143, 1148217708, 1635021673, 6644590, 458758, 20, 3, 24 | 1130327143, 1147956341, 1635021673, 6644590, 196613, 22, 0, 196613, 28, 25 | 5194325, 393222, 28, 0, 2003134838, 1702521171, 0, 196613, 30, 7299701, 262215, 26 | 9, 30, 1, 262215, 11, 30, 1, 262215, 13, 30, 0, 262215, 14, 30, 0, 327752, 20, 27 | 0, 11, 0, 327752, 20, 1, 11, 1, 327752, 20, 2, 11, 3, 327752, 20, 3, 11, 4, 28 | 196679, 20, 2, 327752, 28, 0, 35, 0, 196679, 28, 2, 262215, 30, 34, 0, 262215, 29 | 30, 33, 0, 131091, 2, 196641, 3, 2, 196630, 6, 32, 262167, 7, 6, 2, 262176, 8, 30 | 3, 7, 262203, 8, 9, 3, 262176, 10, 1, 7, 262203, 10, 11, 1, 262203, 8, 13, 3, 31 | 262203, 10, 14, 1, 262167, 16, 6, 4, 262165, 17, 32, 0, 262187, 17, 18, 1, 32 | 262172, 19, 6, 18, 393246, 20, 16, 6, 19, 19, 262176, 21, 3, 20, 262203, 21, 33 | 22, 3, 262165, 23, 32, 1, 262187, 23, 24, 0, 262187, 6, 25, 1073741824, 196638, 34 | 28, 7, 262176, 29, 2, 28, 262203, 29, 30, 2, 262176, 31, 2, 7, 262187, 6, 35, 35 | 1065353216, 262187, 6, 38, 0, 262176, 42, 3, 16, 327734, 2, 4, 0, 3, 131320, 5, 36 | 262205, 7, 12, 11, 196670, 9, 12, 262205, 7, 15, 14, 196670, 13, 15, 262205, 7, 37 | 26, 14, 327822, 7, 27, 26, 25, 327745, 31, 32, 30, 24, 262205, 7, 33, 32, 38 | 327816, 7, 34, 27, 33, 327760, 7, 36, 35, 35, 327811, 7, 37, 34, 36, 327761, 6, 39 | 39, 37, 0, 327761, 6, 40, 37, 1, 458832, 16, 41, 39, 40, 38, 35, 327745, 42, 40 | 43, 22, 24, 196670, 43, 41, 65789, 65592 41 | }; 42 | 43 | #endif //header guard -------------------------------------------------------------------------------- /src/nanovg_vk.h: -------------------------------------------------------------------------------- 1 | #ifndef VVG_INCLUDE_NANOVG_VK_H 2 | #define VVG_INCLUDE_NANOVG_VK_H 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | typedef struct NVGcontext NVGcontext; 13 | 14 | /// Description for a vulkan nanovg context. 15 | typedef struct VVGContextDescription { 16 | VkInstance instance; // the instance to create the context for 17 | VkPhysicalDevice phDev; // the physical device to create the context for 18 | VkDevice device; // the device to create the context for. Must match given instance and phDev 19 | VkQueue queue; // the queue that should be use for rendering. Must support graphcis ops. 20 | unsigned int queueFamily; // the queue family of the given queue. 21 | VkSwapchainKHR swapchain; // the swapchain on which should be rendered. 22 | VkExtent2D swapchainSize; // the size of the given swapchain 23 | VkFormat swapchainFormat; // the format of the given swapchain 24 | } VVGContextDescription; 25 | 26 | /// This function can be called to create a new nanovg vulkan context that will render 27 | /// on the given swapchain. 28 | NVGcontext* vvgCreate(const VVGContextDescription* description); 29 | 30 | /// Destroys the given nanovg context. 31 | void vvgDestroy(const NVGcontext* ctx); 32 | 33 | #ifdef __cplusplus 34 | } //extern C 35 | #endif 36 | 37 | #endif // header guard 38 | 39 | // Copyright © 2016 nyorain 40 | // 41 | // Permission is hereby granted, free of charge, to any person 42 | // obtaining a copy of this software and associated documentation 43 | // files (the “Software”), to deal in the Software without 44 | // restriction, including without limitation the rights to use, 45 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 46 | // copies of the Software, and to permit persons to whom the 47 | // Software is furnished to do so, subject to the following 48 | // conditions: 49 | // 50 | // The above copyright notice and this permission notice shall be 51 | // included in all copies or substantial portions of the Software. 52 | // 53 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 54 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 55 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 56 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 57 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 58 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 59 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 60 | // OTHER DEALINGS IN THE SOFTWARE. 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Vulkan Vector Graphics 2 | 3 | __Currently not maintained (and not compiling). 4 | Lacks many important features to implement the nanovg interface correctly and does not make efficient use of vulkan. 5 | Might only be useful to you as starting point if you want to implement a vulkan nanovg backend yourself (which is not a good idea imo now). 6 | I started [rvg](https://github.com/nyorain/rvg) as a more efficient and working retained mode alternative.__ 7 | 8 | vvg is a __[nanovg]__ backend using the [vulkan] api licensed under the __MIT License__. 9 | It offers 2 interfaces: 10 | 11 | - A [high level interface] that can be accessed from plain C99 and just provides the nanovg backend. 12 | - A [lower level interface] written in C++14 that can be used for complex tasks and is perfectly integrated with [vpp]. 13 | 14 | The implementation itself is written in C++14 and uses the [vpp] library. It also makes use of the [bintoheader] tool 15 | to include the compiles spirv binaries directly into the source code. See for example [src/shader/headers/fill.frag.h]. 16 | Any bug reports, contributions and ideas are highly appreciated. 17 | 18 | ### Usage 19 | 20 | You have to choose if you want to use the C or the C++ api. 21 | You can either compile vvg to a static or shared library or compile its single source file 22 | and the [nanovg] source file with your program/library. Note that the implementation needs to be linked to the latest 23 | [vpp] if you choose to just built the implementation source togehter with you project. 24 | 25 | Building can be done using meson. 26 | 27 | Either install or copy the needed header ([nanovg_vk.h] for the C api or [vvg.hpp] for the C++ api) as 28 | well as the [nanovg] header which can also be found in the [src/] directory (nanovg.h, needed to actually draw something, 29 | vvg is only the backend). 30 | For the C++ api make sure that the [vpp] headers are in your include path. 31 | For the C api make sure that you include vulkan.h BEFORE including the vvg header. You still have to link with [vpp]. 32 | 33 | For an example how to use it see [examples/example.cpp]. 34 | It has only support for Windows at the moment (since it creates a window for rendering). 35 | 36 | [examples/example.cpp]: examples/example.cpp 37 | [vulkan]: https://www.khronos.org/vulkan/ 38 | [high level interface]: src/nanovg_vk.h 39 | [nanovg_vk.h]: src/nanovg_vk.h 40 | [lower level interface]: src/vvg.hpp 41 | [vvg.hpp]: src/vvg.hpp 42 | [src/]: src/ 43 | [bintoheader]: https://github.com/nyorain/bintoheader 44 | [vpp]: https://github.com/nyorain/vpp 45 | [nanovg]: https://github.com/memononen/nanovg 46 | [src/shader/headers/fill.frag.h]: src/shader/headers/fill.frag.h 47 | -------------------------------------------------------------------------------- /src/shader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # bintoheader 2 | ExternalProject_Add(bintoheader_ep 3 | PREFIX ${CMAKE_BINARY_DIR}/bintoheader 4 | GIT_REPOSITORY https://github.com/nyorain/bintoheader.git 5 | GIT_TAG master 6 | INSTALL_DIR ${CMAKE_BINARY_DIR}/external/install 7 | CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external/install) 8 | 9 | ExternalProject_Add_Step(bintoheader_ep 10 | forceinstall 11 | DEPENDEES configure 12 | DEPENDERS install 13 | ALWAYS 1) 14 | 15 | # add_shader 16 | function(add_shader name project) 17 | add_custom_command( 18 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.spv" 19 | COMMAND glslangValidator 20 | -V "${CMAKE_CURRENT_SOURCE_DIR}/${name}" 21 | -o "${CMAKE_CURRENT_BINARY_DIR}/${name}.spv" 22 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${name}" 23 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" 24 | COMMENT "Building spirv shader - ${name}") 25 | 26 | add_custom_target(shader_${name} 27 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${name}.spv") 28 | add_dependencies(${project} shader_${name}) 29 | endfunction(add_shader) 30 | 31 | # add_shader_header 32 | function(add_shader_header name project) 33 | add_shader(${name} ${project}) 34 | add_custom_command( 35 | OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/${name}.spv.h" 36 | COMMAND "${CMAKE_BINARY_DIR}/external/install/bin/bintoheader" 37 | -i "${CMAKE_CURRENT_BINARY_DIR}/${name}.spv" 38 | -o "${CMAKE_CURRENT_SOURCE_DIR}/${name}.h" 39 | -n ${name}_data 40 | -s 32 41 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${name}.spv" 42 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 43 | COMMENT "Transforming spirv ${name} into C header") 44 | 45 | add_custom_target(shader_header_${name} 46 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${name}.spv.h") 47 | add_dependencies(shader_header_${name} shader_${name}) 48 | add_dependencies(${project} shader_header_${name}) 49 | 50 | if(Depend) 51 | add_dependencies(shader_header_${name} bintoheader_ep) 52 | endif() 53 | endfunction(add_shader_header) 54 | 55 | 56 | # shaders 57 | function(add_shader2 name project) 58 | add_custom_command( 59 | OUTPUT "${CMAKE_BINARY_DIR}/${name}.spv" 60 | COMMAND glslangValidator 61 | -V "${CMAKE_CURRENT_SOURCE_DIR}/${name}" 62 | -o "${name}.spv" 63 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${name}" 64 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" 65 | COMMENT "Building spirv shader - ${name}") 66 | string(REGEX REPLACE \\\. _ array_name ${name}) 67 | add_custom_command( 68 | OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/${name}.h" 69 | COMMAND ${CMAKE_BINARY_DIR}/external/install/bin/bintoheader 70 | -i "${CMAKE_CURRENT_BINARY_DIR}/${name}.spv" 71 | -o "${CMAKE_CURRENT_SOURCE_DIR}/${name}.h" 72 | -s 32 73 | -n ${array_name}_data 74 | DEPENDS "${CMAKE_BINARY_DIR}/${name}.spv" 75 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 76 | COMMENT "Generating spirv header - ${name}") 77 | add_custom_target(shader_${name} ALL 78 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${name}.h") 79 | add_dependencies(${project} shader_${name}) 80 | endfunction() 81 | 82 | # add_shader_header("fill.frag" vvg) 83 | # add_shader_header("fill.vert" vvg) 84 | 85 | add_shader2("fill.frag" vvg) 86 | add_shader2("fill.vert" vvg) 87 | -------------------------------------------------------------------------------- /src/shader/fill.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_ARB_separate_shader_objects : enable 4 | #extension GL_ARB_shading_language_420pack : enable 5 | 6 | #define TYPE_COLOR 1 7 | #define TYPE_GRADIENT 2 8 | #define TYPE_TEXTURE 3 9 | 10 | #define TEXTYPE_RGBA 1 11 | #define TEXTYPE_A 2 12 | 13 | #define strokeThr -1.0f 14 | 15 | layout(constant_id = 0) const bool edgeAntiAlias = true; 16 | 17 | layout(location = 0) in vec2 ipos; 18 | layout(location = 1) in vec2 itexcoord; 19 | 20 | layout(location = 0) out vec4 ocolor; 21 | 22 | layout(set = 0, binding = 0) uniform UBO 23 | { 24 | //not used here; from vertex shader 25 | vec2 viewSize; //0 26 | 27 | //type to draw (TYPE_* macros) 28 | uint type; //8 29 | 30 | //type of the texture (if type is TYPE_TEXTURE) 31 | uint texType; //12 32 | 33 | //two colors values 34 | vec4 innerColor; //16 35 | vec4 outerColor; //32 36 | 37 | //mat3 is used as matrix. 38 | //mat[3][0;1] is used as scissor extent 39 | //mat[3][2;3] is used as scissor scale 40 | //mat[0][3] is used as radius 41 | //mat[1][3] is used as feather 42 | //mat[2][3] is used as strokeWidth 43 | mat4 scissorMat; //48 44 | 45 | //mat3 is used as matrix 46 | //mat[3][0;1] is used as extent 47 | //mat[3][2;3] is FREE 48 | //mat[0][3] is used as strokeMult 49 | //mat[1][3] is FREE 50 | //mat[2][3] is FREE 51 | mat4 paintMat; //112 52 | 53 | } ubo; 54 | 55 | layout(set = 0, binding = 1) uniform sampler2D tex; //for texture drawing 56 | 57 | float sdroundrect(vec2 pt, vec2 ext, float rad) 58 | { 59 | vec2 ext2 = ext - vec2(rad, rad); 60 | vec2 d = abs(pt) - ext2; 61 | return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - rad; 62 | } 63 | 64 | float scissorMask(vec2 pos) 65 | { 66 | vec2 sc = (abs((mat3(ubo.scissorMat) * vec3(pos, 1.0)).xy) - vec2(ubo.scissorMat[3])); 67 | sc = vec2(0.5, 0.5) - sc * vec2(ubo.scissorMat[3][2], ubo.scissorMat[3][3]); 68 | return clamp(sc.x, 0.0, 1.0) * clamp(sc.y, 0.0, 1.0); 69 | } 70 | 71 | float strokeMask() 72 | { 73 | float strokeMult = ubo.paintMat[0][3]; 74 | return min(1.0, (1.0 - abs(itexcoord.x * 2.0 - 1.0)) * strokeMult) * min(1.0, itexcoord.y); 75 | } 76 | 77 | void main() 78 | { 79 | float scissorAlpha = scissorMask(ipos); 80 | if(edgeAntiAlias && scissorAlpha < 0.5f) discard; 81 | 82 | float strokeAlpha = strokeMask(); 83 | if(strokeAlpha < strokeThr) discard; 84 | 85 | if(ubo.type == TYPE_COLOR) 86 | { 87 | ocolor = ubo.innerColor; 88 | if(edgeAntiAlias) ocolor *= strokeAlpha; 89 | } 90 | else if(ubo.type == TYPE_GRADIENT) 91 | { 92 | vec2 pt = (mat3(ubo.paintMat) * vec3(ipos, 1.0)).xy; 93 | // vec2 pt = ipos; 94 | float ft = ubo.scissorMat[1][3]; 95 | // float fac = sdroundrect(pt, vec2(ubo.paintMat[3]), ubo.scissorMat[0][3]) / ft; 96 | // ocolor = mix(ubo.innerColor, ubo.outerColor, clamp(0.5 + fac, 0.0, 1.0)); 97 | // // ocolor = vec4(1.0, 0.0, 1.0, 1.0); 98 | // 99 | vec2 extent = vec2(ubo.paintMat[3][0], ubo.paintMat[3][1]); 100 | float radius = ubo.scissorMat[0][3]; 101 | float d = clamp((sdroundrect(pt, extent, radius) + ft*0.5) / ft, 0.0, 1.0); 102 | ocolor = mix(ubo.innerColor,ubo.outerColor,d); 103 | // ocolor = vec4(radius, extent.x, extent.y, 1.0); 104 | if(edgeAntiAlias) ocolor *= strokeAlpha; 105 | } 106 | else if(ubo.type == TYPE_TEXTURE) 107 | { 108 | ocolor = texture(tex, itexcoord); 109 | if(ubo.texType == TEXTYPE_RGBA) ocolor = vec4(ocolor.xyz * ocolor.w, ocolor.w); 110 | else if(ubo.texType == TEXTYPE_A) ocolor = vec4(ocolor.x); 111 | ocolor = ocolor * ubo.innerColor; 112 | } 113 | 114 | ocolor *= scissorAlpha; 115 | } 116 | -------------------------------------------------------------------------------- /examples/basic-ny-gl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "nanovg.h" 5 | 6 | // GL example for comparison with the vulkan example 7 | 8 | #define NANOVG_GL_IMPLEMENTATION 1 9 | #define NANOVG_GL3 1 10 | #define GL_GLEXT_PROTOTYPES 11 | #include 12 | #include 13 | #include "nanovg-gl.h" 14 | 15 | class MyWindowListener : public ny::WindowListener { 16 | public: 17 | bool* run; 18 | 19 | void close(const ny::CloseEvent& ev) override { 20 | *run = false; 21 | } 22 | void key(const ny::KeyEvent& ev) override { 23 | if(ev.pressed && ev.keycode == ny::Keycode::escape) 24 | *run = false; 25 | } 26 | }; 27 | 28 | int main() 29 | { 30 | constexpr auto width = 1200u; 31 | constexpr auto height = 800u; 32 | 33 | // init ny app 34 | auto& backend = ny::Backend::choose(); 35 | if(!backend.vulkan()) { 36 | dlg_error("ny backend has no vulkan support!"); 37 | return 0; 38 | } 39 | 40 | auto ac = backend.createAppContext(); 41 | 42 | // ny init 43 | auto run = true; 44 | 45 | auto listener = MyWindowListener {}; 46 | listener.run = &run; 47 | 48 | auto ws = ny::WindowSettings {}; 49 | ny::GlSurface* surf {}; 50 | ws.surface = ny::SurfaceType::gl; 51 | ws.listener = &listener; 52 | ws.size = {width, height}; 53 | ws.gl.storeSurface = &surf; 54 | auto wc = ac->createWindowContext(ws); 55 | 56 | auto context = ac->glSetup()->createContext(); 57 | context->makeCurrent(*surf); 58 | 59 | // vvg setup 60 | auto nvgContext = nvgCreateGL3(NVG_ANTIALIAS | NVG_DEBUG); 61 | auto font = nvgCreateFont(nvgContext, "sans", "Roboto-Regular.ttf"); 62 | 63 | using Clock = std::chrono::high_resolution_clock; 64 | auto lastFrameTimer = Clock::now(); 65 | unsigned int framesCount = 0; 66 | std::string fpsString = "420 fps"; 67 | 68 | // main loop 69 | while(run) { 70 | if(!ac->dispatchEvents()) 71 | break; 72 | 73 | glClearColor(0.f, 0.f, 0.f, 1.0f); 74 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 75 | 76 | nvgBeginFrame(nvgContext, width, height, width / (float) height); 77 | 78 | nvgBeginPath(nvgContext); 79 | nvgMoveTo(nvgContext, 10, 10); 80 | nvgLineTo(nvgContext, 10, 400); 81 | nvgLineTo(nvgContext, 100, 400); 82 | nvgQuadTo(nvgContext, 100, 50, 400, 120); 83 | nvgLineTo(nvgContext, 450, 10); 84 | nvgClosePath(nvgContext); 85 | 86 | nvgFillColor(nvgContext, nvgRGBAf(0.5, 0.8, 0.7, 1.0)); 87 | nvgFill(nvgContext); 88 | 89 | nvgBeginPath(nvgContext); 90 | nvgFontFaceId(nvgContext, font); 91 | nvgFontSize(nvgContext, 100.f); 92 | nvgFontBlur(nvgContext, .8f); 93 | nvgFillColor(nvgContext, nvgRGBAf(1.0, 1.0, 1.0, 1.0)); 94 | nvgTextBox(nvgContext, 200, 200, width - 200, "Hello Vulkan Vector Graphics World", nullptr); 95 | 96 | nvgFontSize(nvgContext, 30.f); 97 | nvgFontBlur(nvgContext, .2f); 98 | nvgText(nvgContext, 10, height - 20, fpsString.c_str(), nullptr); 99 | 100 | nvgBeginPath(nvgContext); 101 | nvgPathWinding(nvgContext, NVG_HOLE); 102 | nvgRect(nvgContext, 700, 400, 300, 300); 103 | nvgPathWinding(nvgContext, NVG_HOLE); 104 | // nvgRect(nvgContext, 750, 450, 50, 50); 105 | // nvgPathWinding(nvgContext, NVG_SOLID); 106 | // nvgRect(nvgContext, 750, 450, 50, 50); 107 | 108 | // auto paint = nvgRadialGradient(nvgContext, 750, 425,20, 50, nvgRGB(0, 0, 200), nvgRGB(200, 200, 0)); 109 | // auto paint = nvgRadialGradient(nvgContext, 0.0, 0.0, 0.2, 100.0, nvgRGB(0, 0, 200), nvgRGB(200, 200, 0)); 110 | auto paint = nvgLinearGradient(nvgContext, 700, 400, 800, 450, nvgRGB(0, 0, 200), nvgRGB(200, 200, 0)); 111 | nvgFillPaint(nvgContext, paint); 112 | // nvgFillColor(nvgContext, nvgRGBA(200, 200, 0, 200)); 113 | nvgClosePath(nvgContext); 114 | nvgFill(nvgContext); 115 | 116 | nvgEndFrame(nvgContext); 117 | surf->apply(); 118 | 119 | // only refresh frame timer every second 120 | framesCount++; 121 | if(Clock::now() - lastFrameTimer >= std::chrono::seconds(1)) { 122 | fpsString = std::to_string(framesCount) + " fps"; 123 | lastFrameTimer = Clock::now(); 124 | framesCount = 0; 125 | } 126 | } 127 | 128 | nvgDeleteGL3(nvgContext); 129 | } 130 | -------------------------------------------------------------------------------- /examples/basic-ny.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define WithLayers 11 | 12 | class MyWindowListener : public ny::WindowListener { 13 | public: 14 | bool* run; 15 | 16 | void close(const ny::CloseEvent& ev) override { 17 | *run = false; 18 | } 19 | void key(const ny::KeyEvent& ev) override { 20 | if(ev.pressed && ev.keycode == ny::Keycode::escape) 21 | *run = false; 22 | } 23 | }; 24 | 25 | int main() 26 | { 27 | constexpr auto width = 1200u; 28 | constexpr auto height = 800u; 29 | 30 | // init ny app 31 | auto& backend = ny::Backend::choose(); 32 | if(!backend.vulkan()) { 33 | dlg_error("ny backend has no vulkan support!"); 34 | return 0; 35 | } 36 | 37 | auto ac = backend.createAppContext(); 38 | 39 | // basic vpp init 40 | auto iniExtensions = ac->vulkanExtensions(); 41 | iniExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 42 | vk::ApplicationInfo appInfo ("vpp-intro", 1, "vpp", 1, VK_API_VERSION_1_0); 43 | vk::InstanceCreateInfo instanceInfo; 44 | instanceInfo.pApplicationInfo = &appInfo; 45 | instanceInfo.enabledExtensionCount = iniExtensions.size(); 46 | instanceInfo.ppEnabledExtensionNames = iniExtensions.data(); 47 | 48 | #ifdef WithLayers 49 | constexpr auto layer = "VK_LAYER_LUNARG_standard_validation"; 50 | instanceInfo.enabledLayerCount = 1; 51 | instanceInfo.ppEnabledLayerNames = &layer; 52 | #endif 53 | 54 | vpp::Instance instance(instanceInfo); 55 | 56 | #ifdef WithLayers 57 | vpp::DebugCallback debugCallback(instance); 58 | #endif 59 | 60 | // ny init 61 | auto run = true; 62 | 63 | auto listener = MyWindowListener {}; 64 | listener.run = &run; 65 | 66 | auto vkSurface = vk::SurfaceKHR {}; 67 | auto ws = ny::WindowSettings {}; 68 | ws.surface = ny::SurfaceType::vulkan; 69 | ws.listener = &listener; 70 | ws.size = {width, height}; 71 | ws.vulkan.instance = (VkInstance) instance.vkHandle(); 72 | ws.vulkan.storeSurface = &(std::uintptr_t&) (vkSurface); 73 | auto wc = ac->createWindowContext(ws); 74 | 75 | // further vpp init 76 | const vpp::Queue* presentQueue; 77 | vpp::Device device(instance, vkSurface, presentQueue); 78 | vpp::Swapchain swapchain(device, vkSurface, {width, height}, {}); 79 | 80 | // vvg setup 81 | auto nvgContext = vvg::createContext(swapchain); 82 | auto font = nvgCreateFont(nvgContext, "sans", "Roboto-Regular.ttf"); 83 | 84 | using Clock = std::chrono::high_resolution_clock; 85 | auto lastFrameTimer = Clock::now(); 86 | unsigned int framesCount = 0; 87 | std::string fpsString = "420 fps"; 88 | 89 | // main loop 90 | while(run) { 91 | if(!ac->dispatchEvents()) 92 | break; 93 | 94 | nvgBeginFrame(nvgContext, width, height, width / (float) height); 95 | 96 | nvgBeginPath(nvgContext); 97 | nvgMoveTo(nvgContext, 10, 10); 98 | nvgLineTo(nvgContext, 10, 400); 99 | nvgLineTo(nvgContext, 100, 400); 100 | nvgQuadTo(nvgContext, 100, 50, 400, 120); 101 | nvgLineTo(nvgContext, 450, 10); 102 | nvgClosePath(nvgContext); 103 | 104 | nvgFillColor(nvgContext, nvgRGBAf(0.5, 0.8, 0.7, 1.0)); 105 | nvgFill(nvgContext); 106 | 107 | nvgBeginPath(nvgContext); 108 | nvgFontFaceId(nvgContext, font); 109 | nvgFontSize(nvgContext, 100.f); 110 | nvgFontBlur(nvgContext, .8f); 111 | nvgFillColor(nvgContext, nvgRGBAf(1.0, 1.0, 1.0, 1.0)); 112 | nvgTextBox(nvgContext, 200, 200, width - 200, "Hello Vulkan Vector Graphics World", nullptr); 113 | 114 | nvgFontSize(nvgContext, 30.f); 115 | nvgFontBlur(nvgContext, .2f); 116 | nvgText(nvgContext, 10, height - 20, fpsString.c_str(), nullptr); 117 | 118 | nvgBeginPath(nvgContext); 119 | nvgRect(nvgContext, 700, 400, 300, 300); 120 | nvgPathWinding(nvgContext, NVG_HOLE); 121 | nvgRect(nvgContext, 750, 450, 50, 50); 122 | // auto paint = nvgRadialGradient(nvgContext, 750, 425,20, 50, nvgRGB(0, 0, 200), nvgRGB(200, 200, 0)); 123 | // auto paint = nvgRadialGradient(nvgContext, 0.0, 0.0, 0.2, 100.0, nvgRGB(0, 0, 200), nvgRGB(200, 200, 0)); 124 | auto paint = nvgLinearGradient(nvgContext, 700, 400, 800, 450, nvgRGB(0, 0, 200), nvgRGB(200, 200, 0)); 125 | nvgFillPaint(nvgContext, paint); 126 | // nvgFillColor(nvgContext, nvgRGBA(200, 200, 0, 200)); 127 | nvgClosePath(nvgContext); 128 | nvgFill(nvgContext); 129 | 130 | nvgEndFrame(nvgContext); 131 | 132 | // only refresh frame timer every second 133 | framesCount++; 134 | if(Clock::now() - lastFrameTimer >= std::chrono::seconds(1)) { 135 | fpsString = std::to_string(framesCount) + " fps"; 136 | lastFrameTimer = Clock::now(); 137 | framesCount = 0; 138 | } 139 | } 140 | 141 | vvg::destroyContext(*nvgContext); 142 | } 143 | -------------------------------------------------------------------------------- /examples/nanovg_gl_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | #ifndef NANOVG_GL_UTILS_H 19 | #define NANOVG_GL_UTILS_H 20 | 21 | struct NVGLUframebuffer { 22 | NVGcontext* ctx; 23 | GLuint fbo; 24 | GLuint rbo; 25 | GLuint texture; 26 | int image; 27 | }; 28 | typedef struct NVGLUframebuffer NVGLUframebuffer; 29 | 30 | // Helper function to create GL frame buffer to render to. 31 | void nvgluBindFramebuffer(NVGLUframebuffer* fb); 32 | NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); 33 | void nvgluDeleteFramebuffer(NVGLUframebuffer* fb); 34 | 35 | #endif // NANOVG_GL_UTILS_H 36 | 37 | #ifdef NANOVG_GL_IMPLEMENTATION 38 | 39 | #if defined(NANOVG_GL3) || defined(NANOVG_GLES2) || defined(NANOVG_GLES3) 40 | // FBO is core in OpenGL 3>. 41 | # define NANOVG_FBO_VALID 1 42 | #elif defined(NANOVG_GL2) 43 | // On OS X including glext defines FBO on GL2 too. 44 | # ifdef __APPLE__ 45 | # include 46 | # define NANOVG_FBO_VALID 1 47 | # endif 48 | #endif 49 | 50 | static GLint defaultFBO = -1; 51 | 52 | NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags) 53 | { 54 | #ifdef NANOVG_FBO_VALID 55 | GLint defaultFBO; 56 | GLint defaultRBO; 57 | NVGLUframebuffer* fb = NULL; 58 | 59 | glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); 60 | glGetIntegerv(GL_RENDERBUFFER_BINDING, &defaultRBO); 61 | 62 | fb = (NVGLUframebuffer*)malloc(sizeof(NVGLUframebuffer)); 63 | if (fb == NULL) goto error; 64 | memset(fb, 0, sizeof(NVGLUframebuffer)); 65 | 66 | fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); 67 | 68 | #if defined NANOVG_GL2 69 | fb->texture = nvglImageHandleGL2(ctx, fb->image); 70 | #elif defined NANOVG_GL3 71 | fb->texture = nvglImageHandleGL3(ctx, fb->image); 72 | #elif defined NANOVG_GLES2 73 | fb->texture = nvglImageHandleGLES2(ctx, fb->image); 74 | #elif defined NANOVG_GLES3 75 | fb->texture = nvglImageHandleGLES3(ctx, fb->image); 76 | #endif 77 | 78 | fb->ctx = ctx; 79 | 80 | // frame buffer object 81 | glGenFramebuffers(1, &fb->fbo); 82 | glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo); 83 | 84 | // render buffer object 85 | glGenRenderbuffers(1, &fb->rbo); 86 | glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo); 87 | glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h); 88 | 89 | // combine all 90 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); 91 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); 92 | 93 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error; 94 | 95 | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); 96 | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); 97 | return fb; 98 | error: 99 | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); 100 | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); 101 | nvgluDeleteFramebuffer(fb); 102 | return NULL; 103 | #else 104 | NVG_NOTUSED(ctx); 105 | NVG_NOTUSED(w); 106 | NVG_NOTUSED(h); 107 | NVG_NOTUSED(imageFlags); 108 | return NULL; 109 | #endif 110 | } 111 | 112 | void nvgluBindFramebuffer(NVGLUframebuffer* fb) 113 | { 114 | #ifdef NANOVG_FBO_VALID 115 | if (defaultFBO == -1) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); 116 | glBindFramebuffer(GL_FRAMEBUFFER, fb != NULL ? fb->fbo : defaultFBO); 117 | #else 118 | NVG_NOTUSED(fb); 119 | #endif 120 | } 121 | 122 | void nvgluDeleteFramebuffer(NVGLUframebuffer* fb) 123 | { 124 | #ifdef NANOVG_FBO_VALID 125 | if (fb == NULL) return; 126 | if (fb->fbo != 0) 127 | glDeleteFramebuffers(1, &fb->fbo); 128 | if (fb->rbo != 0) 129 | glDeleteRenderbuffers(1, &fb->rbo); 130 | if (fb->image >= 0) 131 | nvgDeleteImage(fb->ctx, fb->image); 132 | fb->ctx = NULL; 133 | fb->fbo = 0; 134 | fb->rbo = 0; 135 | fb->texture = 0; 136 | fb->image = -1; 137 | free(fb); 138 | #else 139 | NVG_NOTUSED(fb); 140 | #endif 141 | } 142 | 143 | #endif // NANOVG_GL_IMPLEMENTATION 144 | -------------------------------------------------------------------------------- /examples/offscreen.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #define STB_IMAGE_WRITE_IMPLEMENTATION 13 | #include "stb_image_write.h" 14 | 15 | vpp::RenderPass initRenderPass(const vpp::Device& dev, vk::Format format); 16 | 17 | int main() 18 | { 19 | //init vulkan stuff 20 | //dont use a context here since we dont need surface/swapchain 21 | //most of this is taken somehow from vpp/src/context.cpp 22 | 23 | //instance 24 | vk::ApplicationInfo appInfo; 25 | appInfo.pApplicationName = "vvg-offscreen"; 26 | appInfo.applicationVersion = 1; 27 | appInfo.pEngineName = "vvg"; 28 | appInfo.engineVersion = 1; 29 | appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 21); 30 | 31 | constexpr auto layer = "VK_LAYER_LUNARG_standard_validation"; 32 | vk::InstanceCreateInfo iniinfo; 33 | // iniinfo.enabledLayerCount = 1; 34 | // iniinfo.ppEnabledLayerNames = &layer; 35 | iniinfo.enabledExtensionCount = 0; 36 | iniinfo.ppEnabledExtensionNames = nullptr; 37 | iniinfo.pApplicationInfo = &appInfo; 38 | 39 | vpp::Instance instance(iniinfo); 40 | 41 | //device 42 | auto phdevs = vk::enumeratePhysicalDevices(instance); 43 | auto queueProps = vk::getPhysicalDeviceQueueFamilyProperties(phdevs[0]); 44 | 45 | const float prio = 0.0; 46 | vk::DeviceQueueCreateInfo queueInfo; 47 | queueInfo.queueCount = 1; 48 | queueInfo.pQueuePriorities = &prio; 49 | for(auto i = 0u; i < queueProps.size(); ++i) 50 | { 51 | const auto& qProp = queueProps[i]; 52 | queueInfo.queueFamilyIndex = i; 53 | if(qProp.queueFlags & vk::QueueBits::graphics) break; 54 | } 55 | 56 | vk::DeviceCreateInfo devinfo; 57 | devinfo.queueCreateInfoCount = 1; 58 | devinfo.pQueueCreateInfos = &queueInfo; 59 | devinfo.enabledExtensionCount = 0; 60 | devinfo.ppEnabledExtensionNames = nullptr; 61 | 62 | vpp::Device dev(instance, phdevs[0], devinfo); 63 | 64 | //renderPass 65 | auto renderPass = initRenderPass(dev, vk::Format::r8g8b8a8Unorm); 66 | 67 | //framebuffer 68 | auto width = 1024; 69 | auto height = 1024; 70 | 71 | auto colorAttachment = vpp::ViewableImage::defaultColor2D(); 72 | // colorAttachment.imgInfo.extent = {width, height, 1}; 73 | colorAttachment.imgInfo.usage |= vk::ImageUsageBits::colorAttachment | vk::ImageUsageBits::transferSrc; 74 | colorAttachment.imgInfo.format = vk::Format::r8g8b8a8Unorm; 75 | colorAttachment.imgInfo.tiling = vk::ImageTiling::linear; 76 | colorAttachment.viewInfo.format = vk::Format::r8g8b8a8Unorm; 77 | 78 | // colorAttachment.memoryTypeBits = dev.memoryTypeBits(vk::MemoryPropertyBits::hostVisible); 79 | // auto depthAttachment = vpp::ViewableImage::defaultDepth2D(); 80 | 81 | vpp::Framebuffer framebuffer(dev, renderPass, {width, height}, 82 | {colorAttachment}); 83 | 84 | //create the nanovg context 85 | auto nvgContext = vvg::createContext(framebuffer, renderPass); 86 | nvgBeginFrame(nvgContext, width, height, width / (float)height); 87 | 88 | nvgBeginPath(nvgContext); 89 | nvgMoveTo(nvgContext, 10, 10); 90 | nvgLineTo(nvgContext, 10, 400); 91 | nvgLineTo(nvgContext, 100, 400); 92 | nvgQuadTo(nvgContext, 100, 50, 400, 120); 93 | nvgLineTo(nvgContext, 450, 10); 94 | nvgClosePath(nvgContext); 95 | nvgFillColor(nvgContext, nvgRGBAf(0.5, 0.8, 0.7, 0.7)); 96 | nvgFill(nvgContext); 97 | 98 | nvgEndFrame(nvgContext); 99 | 100 | vvg::destroyContext(*nvgContext); 101 | 102 | //write the image to a file 103 | auto layout = vk::ImageLayout::presentSrcKHR; 104 | auto ptr = vpp::retrieve(framebuffer.attachments()[0].image(), layout, 105 | vk::Format::r8g8b8a8Unorm, {width, height, 1}, {vk::ImageAspectBits::color, 0, 1}); 106 | auto& data = *ptr->data().data(); 107 | return stbi_write_png("test1.png", width, height, 4, &data, width * 4); 108 | } 109 | 110 | vpp::RenderPass initRenderPass(const vpp::Device& dev, vk::Format format) 111 | { 112 | vk::AttachmentDescription attachments[1] {}; 113 | 114 | //color from swapchain 115 | attachments[0].format = format; 116 | attachments[0].samples = vk::SampleCountBits::e1; 117 | attachments[0].loadOp = vk::AttachmentLoadOp::clear; 118 | attachments[0].storeOp = vk::AttachmentStoreOp::store; 119 | attachments[0].stencilLoadOp = vk::AttachmentLoadOp::dontCare; 120 | attachments[0].stencilStoreOp = vk::AttachmentStoreOp::dontCare; 121 | attachments[0].initialLayout = vk::ImageLayout::undefined; 122 | attachments[0].finalLayout = vk::ImageLayout::presentSrcKHR; 123 | 124 | vk::AttachmentReference colorReference; 125 | colorReference.attachment = 0; 126 | colorReference.layout = vk::ImageLayout::colorAttachmentOptimal; 127 | 128 | //depth from own depth stencil 129 | // attachments[1].format = vk::Format::d16UnormS8Uint; 130 | // attachments[1].samples = vk::SampleCountBits::e1; 131 | // attachments[1].loadOp = vk::AttachmentLoadOp::clear; 132 | // attachments[1].storeOp = vk::AttachmentStoreOp::store; 133 | // attachments[1].stencilLoadOp = vk::AttachmentLoadOp::dontCare; 134 | // attachments[1].stencilStoreOp = vk::AttachmentStoreOp::dontCare; 135 | // attachments[1].initialLayout = vk::ImageLayout::undefined; 136 | // attachments[1].finalLayout = vk::ImageLayout::undefined; 137 | // attachments[1].initialLayout = vk::ImageLayout::depthStencilAttachmentOptimal; 138 | // attachments[1].finalLayout = vk::ImageLayout::depthStencilAttachmentOptimal; 139 | 140 | // vk::AttachmentReference depthReference; 141 | // depthReference.attachment = 1; 142 | // depthReference.layout = vk::ImageLayout::depthStencilAttachmentOptimal; 143 | 144 | //only subpass 145 | vk::SubpassDescription subpass; 146 | subpass.pipelineBindPoint = vk::PipelineBindPoint::graphics; 147 | subpass.flags = {}; 148 | subpass.inputAttachmentCount = 0; 149 | subpass.pInputAttachments = nullptr; 150 | subpass.colorAttachmentCount = 1; 151 | subpass.pColorAttachments = &colorReference; 152 | subpass.pResolveAttachments = nullptr; 153 | subpass.pDepthStencilAttachment = nullptr; 154 | subpass.preserveAttachmentCount = 0; 155 | subpass.pPreserveAttachments = nullptr; 156 | 157 | vk::RenderPassCreateInfo renderPassInfo; 158 | renderPassInfo.attachmentCount = 1; 159 | renderPassInfo.pAttachments = attachments; 160 | renderPassInfo.subpassCount = 1; 161 | renderPassInfo.pSubpasses = &subpass; 162 | renderPassInfo.dependencyCount = 0; 163 | renderPassInfo.pDependencies = nullptr; 164 | 165 | return {dev, renderPassInfo}; 166 | } 167 | -------------------------------------------------------------------------------- /examples/basic-windows.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // This example uses vpp to setup vulkan context and swapchain 4 | #include 5 | #include 6 | #include 7 | 8 | // This header is always needed to draw something (nanovg context) 9 | #include 10 | 11 | #include //include this header for the more detailed c++ api 12 | //#include //include this header for the high lvl c api 13 | 14 | constexpr auto width = 900; 15 | constexpr auto height = 500; 16 | 17 | LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); 18 | HWND createWindow(); 19 | 20 | int main() 21 | { 22 | std::ios_base::sync_with_stdio(false); //especially useful with buggy vulkan layers... 23 | auto window = createWindow(); 24 | 25 | // auto vc = vpp::createContext(window, {width, height}); //default layers and debug flags 26 | auto vc = vpp::createContext(window, {width, height, {}}); //layers disabled 27 | 28 | // The easy way to create a nanovg context directly from the vpp swapchain 29 | auto nvgContext = vvg::createContext(vc.swapchain()); 30 | 31 | // create a nanovg font for text and fps counter 32 | auto font = nvgCreateFont(nvgContext, "sans", "Roboto-Regular.ttf"); 33 | 34 | //frame timer stuff 35 | using Clock = std::chrono::high_resolution_clock; 36 | auto lastFrameTimer = Clock::now(); 37 | unsigned int framesCount = 0; 38 | std::string fpsString = "420 fps"; 39 | 40 | //mainloop 41 | while(true) 42 | { 43 | //dispatch windows messages 44 | MSG msg; 45 | PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE); 46 | if(msg.message == WM_QUIT) 47 | { 48 | break; 49 | } 50 | else 51 | { 52 | TranslateMessage(&msg); 53 | DispatchMessage(&msg); 54 | } 55 | 56 | //render 57 | nvgBeginFrame(nvgContext, width, height, width / (float)height); 58 | nvgBeginPath(nvgContext); 59 | nvgMoveTo(nvgContext, 10, 10); 60 | nvgLineTo(nvgContext, 10, 400); 61 | nvgLineTo(nvgContext, 100, 400); 62 | nvgQuadTo(nvgContext, 100, 50, 400, 120); 63 | nvgLineTo(nvgContext, 450, 10); 64 | nvgClosePath(nvgContext); 65 | 66 | nvgFillColor(nvgContext, nvgRGBAf(0.5, 0.8, 0.7, 0.7)); 67 | nvgFill(nvgContext); 68 | 69 | nvgBeginPath(nvgContext); 70 | nvgFontFaceId(nvgContext, font); 71 | nvgFontSize(nvgContext, 100.f); 72 | nvgFontBlur(nvgContext, .8f); 73 | nvgFillColor(nvgContext, nvgRGBAf(1.0, 1.0, 1.0, 1.0)); 74 | nvgTextBox(nvgContext, 200, 200, width - 200, "Hello Vulkan Vector Graphics World", nullptr); 75 | 76 | nvgFontSize(nvgContext, 30.f); 77 | nvgFontBlur(nvgContext, .2f); 78 | nvgText(nvgContext, 10, height - 20, fpsString.c_str(), nullptr); 79 | 80 | nvgBeginPath(nvgContext); 81 | nvgRect(nvgContext, 700, 400, 100, 50); 82 | // auto paint = nvgRadialGradient(nvgContext, 750, 425,20, 50, nvgRGB(0, 0, 200), nvgRGB(200, 200, 0)); 83 | // auto paint = nvgRadialGradient(nvgContext, 0.0, 0.0, 0.2, 100.0, nvgRGB(0, 0, 200), nvgRGB(200, 200, 0)); 84 | auto paint = nvgLinearGradient(nvgContext, 700, 400, 800, 450, nvgRGB(0, 0, 200), nvgRGB(200, 200, 0)); 85 | nvgFillPaint(nvgContext, paint); 86 | // nvgFillColor(nvgContext, nvgRGBA(200, 200, 0, 200)); 87 | nvgClosePath(nvgContext); 88 | nvgFill(nvgContext); 89 | 90 | nvgEndFrame(nvgContext); 91 | 92 | //only refresh frame timer every 50ms 93 | framesCount++; 94 | if(Clock::now() - lastFrameTimer > std::chrono::milliseconds(50)) 95 | { 96 | //we multiply it with 20 since its only the fps count of 50ms 97 | fpsString = std::to_string(framesCount * 20) + " fps"; 98 | lastFrameTimer = Clock::now(); 99 | framesCount = 0; 100 | } 101 | } 102 | 103 | //Note that we have to destruct the context even when using the C++ api since 104 | //the context itself is part of nanovg which is written in C (therefore no RAII). 105 | vvg::destroyContext(*nvgContext); 106 | } 107 | 108 | //The following stuff is not really interesting... 109 | //just windows window creation and mainloop taken from the vpp examples 110 | 111 | LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) 112 | { 113 | switch(message) 114 | { 115 | case WM_ERASEBKGND: 116 | { 117 | //dont erase it to avoid flickering 118 | break; 119 | } 120 | 121 | case WM_CLOSE: 122 | { 123 | DestroyWindow(hwnd); 124 | PostQuitMessage(0); 125 | break; 126 | } 127 | 128 | default: 129 | return DefWindowProc(hwnd, message, wparam, lparam); 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | HWND createWindow() 136 | { 137 | std::string name = "Vulkan Vector Graphics"; 138 | 139 | WNDCLASSEX wndClass; 140 | wndClass.cbSize = sizeof(WNDCLASSEX); 141 | wndClass.style = CS_HREDRAW | CS_VREDRAW; 142 | wndClass.lpfnWndProc = wndProc; 143 | wndClass.cbClsExtra = 0; 144 | wndClass.cbWndExtra = 0; 145 | wndClass.hInstance = GetModuleHandle(nullptr); 146 | wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 147 | wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); 148 | wndClass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); 149 | wndClass.lpszMenuName = NULL; 150 | wndClass.lpszClassName = name.c_str(); 151 | wndClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); 152 | 153 | if (!RegisterClassEx(&wndClass)) 154 | throw std::runtime_error("Failed to register window class"); 155 | 156 | auto flags = WS_EX_COMPOSITED | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_OVERLAPPEDWINDOW; 157 | auto window = CreateWindowEx(0, name.c_str(), name.c_str(), flags, CW_USEDEFAULT, 158 | CW_USEDEFAULT, width, height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr); 159 | 160 | if(!window) 161 | throw std::runtime_error("Failed to create window"); 162 | 163 | COLORREF RRR = RGB(255, 0, 255); 164 | SetLayeredWindowAttributes(window, RRR, (BYTE)0, LWA_COLORKEY); 165 | 166 | ShowWindow(window, SW_SHOW); 167 | SetForegroundWindow(window); 168 | SetFocus(window); 169 | 170 | return window; 171 | } 172 | 173 | // The not-so-fancy way to create a nanovg context from plain vulkan handles. 174 | // VVGContextDescription info; 175 | // info.device = (VkDevice)vulkanContext.device().vkDevice(); 176 | // info.instance = (VkInstance)vulkanContext.vkInstance(); 177 | // info.swapchain = (VkSwapchainKHR)vulkanContext.swapchain().vkHandle(); 178 | // info.swapchainSize = vulkanContext.swapchain().size().vkHandle(); 179 | // info.swapchainFormat = (VkFormat)vulkanContext.swapchain().format(); 180 | // info.phDev = (VkPhysicalDevice)vulkanContext.device().vkPhysicalDevice(); 181 | // info.queue = (VkQueue)vulkanContext.device().queues()[0]->vkHandle(); 182 | // info.queueFamily = vulkanContext.device().queues()[0]->family(); 183 | // auto nvgContext = vvgCreate(&info); 184 | -------------------------------------------------------------------------------- /src/shader/fill.frag.h: -------------------------------------------------------------------------------- 1 | #ifndef BINARY_FILL_FRAG_DATA_HEADER_INCLUDE 2 | #define BINARY_FILL_FRAG_DATA_HEADER_INCLUDE 3 | 4 | #include 5 | 6 | //just check for c++11 7 | #if defined(__cplusplus) && __cplusplus >= 201103L 8 | constexpr 9 | #else 10 | const 11 | #endif 12 | 13 | uint32_t fill_frag_data[] = { 14 | 119734787, 65536, 524289, 292, 0, 131089, 1, 393227, 1, 1280527431, 1685353262, 15 | 808793134, 0, 196622, 0, 1, 524303, 4, 4, 1852399981, 0, 123, 142, 170, 196624, 16 | 4, 7, 196611, 2, 450, 589828, 1096764487, 1935622738, 1918988389, 1600484449, 17 | 1684105331, 1868526181, 1667590754, 29556, 589828, 1096764487, 1935622738, 18 | 1768186216, 1818191726, 1969712737, 1600481121, 1882206772, 7037793, 262149, 4, 19 | 1852399981, 0, 524293, 14, 1869767795, 1919184501, 678716261, 993158774, 20 | 993158774, 3879270, 196613, 11, 29808, 196613, 12, 7632997, 196613, 13, 21 | 6578546, 458757, 18, 1936286579, 1299345267, 678130529, 993158774, 0, 196613, 22 | 17, 7565168, 327685, 21, 1869771891, 1632462187, 2648947, 262149, 23, 23 | 846493797, 0, 196613, 29, 100, 196613, 53, 25459, 196613, 56, 5194325, 393222, 24 | 56, 0, 2003134838, 1702521171, 0, 327686, 56, 1, 1701869940, 0, 327686, 56, 2, 25 | 1417176436, 6647929, 393222, 56, 3, 1701736041, 1819231090, 29295, 393222, 56, 26 | 4, 1702131055, 1819231090, 29295, 393222, 56, 5, 1936286579, 1299345267, 29793, 27 | 393222, 56, 6, 1852399984, 1952533876, 0, 196613, 58, 7299701, 327685, 117, 28 | 1869771891, 1968006507, 29804, 327685, 123, 2019914857, 1919905635, 100, 29 | 393221, 141, 1936286579, 1098018675, 1634234476, 0, 262149, 142, 1936683113, 0, 30 | 262149, 143, 1634886000, 109, 327685, 154, 1869771891, 1816225131, 6383728, 31 | 262149, 170, 1819239279, 29295, 196613, 184, 29808, 196613, 206, 29798, 262149, 32 | 209, 1702131813, 29806, 262149, 215, 1768186226, 29557, 196613, 218, 100, 33 | 262149, 219, 1634886000, 109, 262149, 221, 1634886000, 109, 262149, 223, 34 | 1634886000, 109, 196613, 254, 7890292, 327752, 56, 0, 35, 0, 327752, 56, 1, 35, 35 | 8, 327752, 56, 2, 35, 12, 327752, 56, 3, 35, 16, 327752, 56, 4, 35, 32, 262216, 36 | 56, 5, 5, 327752, 56, 5, 35, 48, 327752, 56, 5, 7, 16, 262216, 56, 6, 5, 37 | 327752, 56, 6, 35, 112, 327752, 56, 6, 7, 16, 196679, 56, 2, 262215, 58, 34, 0, 38 | 262215, 58, 33, 0, 262215, 123, 30, 1, 262215, 142, 30, 0, 262215, 147, 1, 0, 39 | 262215, 170, 30, 0, 262215, 254, 34, 0, 262215, 254, 33, 1, 131091, 2, 196641, 40 | 3, 2, 196630, 6, 32, 262167, 7, 6, 2, 262176, 8, 7, 7, 262176, 9, 7, 6, 393249, 41 | 10, 6, 8, 8, 9, 262177, 16, 6, 8, 196641, 20, 6, 262165, 34, 32, 0, 262187, 34, 42 | 35, 0, 262187, 34, 38, 1, 262187, 6, 42, 0, 262167, 54, 6, 4, 262168, 55, 54, 43 | 4, 589854, 56, 7, 34, 34, 54, 54, 55, 55, 262176, 57, 2, 56, 262203, 57, 58, 2, 44 | 262165, 59, 32, 1, 262187, 59, 60, 5, 262176, 61, 2, 55, 262167, 64, 6, 3, 45 | 262168, 65, 64, 3, 262187, 6, 66, 1065353216, 262187, 59, 87, 3, 262176, 88, 2, 46 | 54, 262187, 6, 95, 1056964608, 327724, 7, 96, 95, 95, 262187, 34, 98, 2, 47 | 262176, 99, 2, 6, 262187, 34, 102, 3, 262187, 59, 118, 6, 262187, 59, 119, 0, 48 | 262176, 122, 1, 7, 262203, 122, 123, 1, 262176, 124, 1, 6, 262187, 6, 127, 49 | 1073741824, 262203, 122, 142, 1, 131092, 146, 196656, 146, 147, 262187, 6, 157, 50 | 3212836864, 262187, 59, 162, 1, 262176, 163, 2, 34, 262176, 169, 3, 54, 262203, 51 | 169, 170, 3, 262187, 59, 234, 4, 589849, 251, 6, 1, 0, 0, 0, 1, 0, 196635, 252, 52 | 251, 262176, 253, 0, 252, 262203, 253, 254, 0, 262187, 59, 258, 2, 262176, 266, 53 | 3, 6, 327734, 2, 4, 0, 3, 131320, 5, 262203, 9, 141, 7, 262203, 8, 143, 7, 54 | 262203, 9, 154, 7, 262203, 8, 184, 7, 262203, 9, 206, 7, 262203, 8, 209, 7, 55 | 262203, 9, 215, 7, 262203, 9, 218, 7, 262203, 8, 219, 7, 262203, 8, 221, 7, 56 | 262203, 9, 223, 7, 262205, 7, 144, 142, 196670, 143, 144, 327737, 6, 145, 18, 57 | 143, 196670, 141, 145, 262205, 6, 148, 141, 327864, 146, 149, 148, 95, 327847, 58 | 146, 150, 147, 149, 196855, 152, 0, 262394, 150, 151, 152, 131320, 151, 65788, 59 | 131320, 152, 262201, 6, 155, 21, 196670, 154, 155, 262205, 6, 156, 154, 327864, 60 | 146, 158, 156, 157, 196855, 160, 0, 262394, 158, 159, 160, 131320, 159, 65788, 61 | 131320, 160, 327745, 163, 164, 58, 162, 262205, 34, 165, 164, 327850, 146, 166, 62 | 165, 38, 196855, 168, 0, 262394, 166, 167, 178, 131320, 167, 327745, 88, 171, 63 | 58, 87, 262205, 54, 172, 171, 196670, 170, 172, 196855, 174, 0, 262394, 147, 64 | 173, 174, 131320, 173, 262205, 6, 175, 154, 262205, 54, 176, 170, 327822, 54, 65 | 177, 176, 175, 196670, 170, 177, 131321, 174, 131320, 174, 131321, 168, 131320, 66 | 178, 327745, 163, 179, 58, 162, 262205, 34, 180, 179, 327850, 146, 181, 180, 67 | 98, 196855, 183, 0, 262394, 181, 182, 245, 131320, 182, 327745, 61, 185, 58, 68 | 118, 262205, 55, 186, 185, 393297, 6, 187, 186, 0, 0, 393297, 6, 188, 186, 0, 69 | 1, 393297, 6, 189, 186, 0, 2, 393297, 6, 190, 186, 1, 0, 393297, 6, 191, 186, 70 | 1, 1, 393297, 6, 192, 186, 1, 2, 393297, 6, 193, 186, 2, 0, 393297, 6, 194, 71 | 186, 2, 1, 393297, 6, 195, 186, 2, 2, 393296, 64, 196, 187, 188, 189, 393296, 72 | 64, 197, 190, 191, 192, 393296, 64, 198, 193, 194, 195, 393296, 65, 199, 196, 73 | 197, 198, 262205, 7, 200, 142, 327761, 6, 201, 200, 0, 327761, 6, 202, 200, 1, 74 | 393296, 64, 203, 201, 202, 66, 327825, 64, 204, 199, 203, 458831, 7, 205, 204, 75 | 204, 0, 1, 196670, 184, 205, 458817, 99, 207, 58, 60, 162, 102, 262205, 6, 208, 76 | 207, 196670, 206, 208, 458817, 99, 210, 58, 118, 87, 35, 262205, 6, 211, 210, 77 | 458817, 99, 212, 58, 118, 87, 38, 262205, 6, 213, 212, 327760, 7, 214, 211, 78 | 213, 196670, 209, 214, 458817, 99, 216, 58, 60, 119, 102, 262205, 6, 217, 216, 79 | 196670, 215, 217, 262205, 7, 220, 184, 196670, 219, 220, 262205, 7, 222, 209, 80 | 196670, 221, 222, 262205, 6, 224, 215, 196670, 223, 224, 458809, 6, 225, 14, 81 | 219, 221, 223, 262205, 6, 226, 206, 327813, 6, 227, 226, 95, 327809, 6, 228, 82 | 225, 227, 262205, 6, 229, 206, 327816, 6, 230, 228, 229, 524300, 6, 231, 1, 43, 83 | 230, 42, 66, 196670, 218, 231, 327745, 88, 232, 58, 87, 262205, 54, 233, 232, 84 | 327745, 88, 235, 58, 234, 262205, 54, 236, 235, 262205, 6, 237, 218, 458832, 85 | 54, 238, 237, 237, 237, 237, 524300, 54, 239, 1, 46, 233, 236, 238, 196670, 86 | 170, 239, 196855, 241, 0, 262394, 147, 240, 241, 131320, 240, 262205, 6, 242, 87 | 154, 262205, 54, 243, 170, 327822, 54, 244, 243, 242, 196670, 170, 244, 131321, 88 | 241, 131320, 241, 131321, 183, 131320, 245, 327745, 163, 246, 58, 162, 262205, 89 | 34, 247, 246, 327850, 146, 248, 247, 102, 196855, 250, 0, 262394, 248, 249, 90 | 250, 131320, 249, 262205, 252, 255, 254, 262205, 7, 256, 123, 327767, 54, 257, 91 | 255, 256, 196670, 170, 257, 327745, 163, 259, 58, 258, 262205, 34, 260, 259, 92 | 327850, 146, 261, 260, 38, 196855, 263, 0, 262394, 261, 262, 276, 131320, 262, 93 | 262205, 54, 264, 170, 524367, 64, 265, 264, 264, 0, 1, 2, 327745, 266, 267, 94 | 170, 102, 262205, 6, 268, 267, 327822, 64, 269, 265, 268, 327745, 266, 270, 95 | 170, 102, 262205, 6, 271, 270, 327761, 6, 272, 269, 0, 327761, 6, 273, 269, 1, 96 | 327761, 6, 274, 269, 2, 458832, 54, 275, 272, 273, 274, 271, 196670, 170, 275, 97 | 131321, 263, 131320, 276, 327745, 163, 277, 58, 258, 262205, 34, 278, 277, 98 | 327850, 146, 279, 278, 98, 196855, 281, 0, 262394, 279, 280, 281, 131320, 280, 99 | 327745, 266, 282, 170, 35, 262205, 6, 283, 282, 458832, 54, 284, 283, 283, 283, 100 | 283, 196670, 170, 284, 131321, 281, 131320, 281, 131321, 263, 131320, 263, 101 | 262205, 54, 285, 170, 327745, 88, 286, 58, 87, 262205, 54, 287, 286, 327813, 102 | 54, 288, 285, 287, 196670, 170, 288, 131321, 250, 131320, 250, 131321, 183, 103 | 131320, 183, 131321, 168, 131320, 168, 262205, 6, 289, 141, 262205, 54, 290, 104 | 170, 327822, 54, 291, 290, 289, 196670, 170, 291, 65789, 65592, 327734, 6, 14, 105 | 0, 10, 196663, 8, 11, 196663, 8, 12, 196663, 9, 13, 131320, 15, 262203, 8, 23, 106 | 7, 262203, 8, 29, 7, 262205, 7, 24, 12, 262205, 6, 25, 13, 262205, 6, 26, 13, 107 | 327760, 7, 27, 25, 26, 327811, 7, 28, 24, 27, 196670, 23, 28, 262205, 7, 30, 108 | 11, 393228, 7, 31, 1, 4, 30, 262205, 7, 32, 23, 327811, 7, 33, 31, 32, 196670, 109 | 29, 33, 327745, 9, 36, 29, 35, 262205, 6, 37, 36, 327745, 9, 39, 29, 38, 110 | 262205, 6, 40, 39, 458764, 6, 41, 1, 40, 37, 40, 458764, 6, 43, 1, 37, 41, 42, 111 | 262205, 7, 44, 29, 327760, 7, 45, 42, 42, 458764, 7, 46, 1, 40, 44, 45, 393228, 112 | 6, 47, 1, 66, 46, 327809, 6, 48, 43, 47, 262205, 6, 49, 13, 327811, 6, 50, 48, 113 | 49, 131326, 50, 65592, 327734, 6, 18, 0, 16, 196663, 8, 17, 131320, 19, 262203, 114 | 8, 53, 7, 327745, 61, 62, 58, 60, 262205, 55, 63, 62, 393297, 6, 67, 63, 0, 0, 115 | 393297, 6, 68, 63, 0, 1, 393297, 6, 69, 63, 0, 2, 393297, 6, 70, 63, 1, 0, 116 | 393297, 6, 71, 63, 1, 1, 393297, 6, 72, 63, 1, 2, 393297, 6, 73, 63, 2, 0, 117 | 393297, 6, 74, 63, 2, 1, 393297, 6, 75, 63, 2, 2, 393296, 64, 76, 67, 68, 69, 118 | 393296, 64, 77, 70, 71, 72, 393296, 64, 78, 73, 74, 75, 393296, 65, 79, 76, 77, 119 | 78, 262205, 7, 80, 17, 327761, 6, 81, 80, 0, 327761, 6, 82, 80, 1, 393296, 64, 120 | 83, 81, 82, 66, 327825, 64, 84, 79, 83, 458831, 7, 85, 84, 84, 0, 1, 393228, 7, 121 | 86, 1, 4, 85, 393281, 88, 89, 58, 60, 87, 262205, 54, 90, 89, 327761, 6, 91, 122 | 90, 0, 327761, 6, 92, 90, 1, 327760, 7, 93, 91, 92, 327811, 7, 94, 86, 93, 123 | 196670, 53, 94, 262205, 7, 97, 53, 458817, 99, 100, 58, 60, 87, 98, 262205, 6, 124 | 101, 100, 458817, 99, 103, 58, 60, 87, 102, 262205, 6, 104, 103, 327760, 7, 125 | 105, 101, 104, 327813, 7, 106, 97, 105, 327811, 7, 107, 96, 106, 196670, 53, 126 | 107, 327745, 9, 108, 53, 35, 262205, 6, 109, 108, 524300, 6, 110, 1, 43, 109, 127 | 42, 66, 327745, 9, 111, 53, 38, 262205, 6, 112, 111, 524300, 6, 113, 1, 43, 128 | 112, 42, 66, 327813, 6, 114, 110, 113, 131326, 114, 65592, 327734, 6, 21, 0, 129 | 20, 131320, 22, 262203, 9, 117, 7, 458817, 99, 120, 58, 118, 119, 102, 262205, 130 | 6, 121, 120, 196670, 117, 121, 327745, 124, 125, 123, 35, 262205, 6, 126, 125, 131 | 327813, 6, 128, 126, 127, 327811, 6, 129, 128, 66, 393228, 6, 130, 1, 4, 129, 132 | 327811, 6, 131, 66, 130, 262205, 6, 132, 117, 327813, 6, 133, 131, 132, 458764, 133 | 6, 134, 1, 37, 66, 133, 327745, 124, 135, 123, 38, 262205, 6, 136, 135, 458764, 134 | 6, 137, 1, 37, 66, 136, 327813, 6, 138, 134, 137, 131326, 138, 65592 135 | }; 136 | 137 | #endif //header guard -------------------------------------------------------------------------------- /src/vvg.hpp: -------------------------------------------------------------------------------- 1 | //C++14 header defining the interface to create a nanovg vulkan backend. 2 | //License at the bottom of this file. 3 | 4 | #ifndef VVG_INCLUDE_VVG_HPP 5 | #define VVG_INCLUDE_VVG_HPP 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | typedef struct NVGcontext NVGcontext; 17 | typedef struct NVGvertex NVGvertex; 18 | typedef struct NVGpaint NVGpaint; 19 | typedef struct NVGpath NVGpath; 20 | typedef struct NVGscissor NVGscissor; 21 | 22 | /// Vulkan Vector Graphics 23 | namespace vvg { 24 | 25 | struct DrawData; 26 | 27 | // TODO: make work async, e.g. let texture store a work pointer and only finish it when used. 28 | /// Represents a vulkan texture. 29 | /// Can be retrieved from the nanovg texture handle using the associated renderer. 30 | class Texture : public vpp::ResourceReference { 31 | public: 32 | Texture() = default; 33 | Texture(const vpp::Device& dev, unsigned int xid, const vk::Extent2D& size, 34 | vk::Format format, const std::uint8_t* data = nullptr); 35 | ~Texture() = default; 36 | 37 | Texture(Texture&& other) noexcept = default; 38 | Texture& operator=(Texture&& other) noexcept = default; 39 | 40 | ///Updates the texture data at the given position and the given size. 41 | ///Note that the given data is NOT tightly packed but must hold data for the whole texture 42 | ///extent. 43 | void update(const vk::Offset2D& offset, const vk::Extent2D& size, const std::uint8_t& data); 44 | 45 | unsigned int id() const { return id_; } 46 | unsigned int width() const { return width_; } 47 | unsigned int height() const { return height_; } 48 | vk::Format format() const { return format_; } 49 | const vpp::ViewableImage& viewableImage() const { return viewableImage_; } 50 | 51 | const auto& resourceRef() const { return viewableImage_; } 52 | 53 | protected: 54 | vpp::ViewableImage viewableImage_; 55 | vk::Format format_; 56 | unsigned int id_; 57 | unsigned int width_; 58 | unsigned int height_; 59 | }; 60 | 61 | // TODO: how to handle swapchain resizes? 62 | /// The Renderer class implements the nanovg backend for vulkan using the vpp library. 63 | /// It can be used to gain more control over the rendering e.g. to just record the required 64 | /// commands to a given command buffer instead of executing them. 65 | /// It can also be used to retrieve some implementation handles such as buffers, pools and 66 | /// pipelines for custom use. 67 | /// The class works (like the gl nanovg implementation) in an delayed manner, i.e. 68 | /// it just stores all draw calls and only renders them once finish is called. 69 | /// It can either render on a vulkan Swapchain for which it uses the SwapchainRenderer class 70 | /// or directly on a framebuffer, then it uses a plain CommandBuffer. 71 | class Renderer : public vpp::Resource { 72 | public: 73 | Renderer() = default; 74 | Renderer(const vpp::Swapchain& swapchain, const vpp::Queue* presentQueue = {}); 75 | 76 | /// Constructs the Renderer for a vulkan framebuffer that can be rendered to with the given 77 | /// render pass. 78 | Renderer(const vpp::Framebuffer& fb, vk::RenderPass renderPass); 79 | virtual ~Renderer(); 80 | 81 | /// Returns the texture with the given id. 82 | const Texture* texture(unsigned int id) const; 83 | Texture* texture(unsigned int id); 84 | 85 | /// Fills the given paths with the given paint. 86 | void fill(const NVGpaint& paint, const NVGscissor& scissor, float fringe, const float* bounds, 87 | nytl::Span paths); 88 | 89 | /// Stokres the given paths with the given paint. 90 | void stroke(const NVGpaint& paint, const NVGscissor& scissor, float fringe, float strokeWidth, 91 | nytl::Span paths); 92 | 93 | /// Renders the given vertices as triangle lists with the given paint. 94 | void triangles(const NVGpaint& paint, const NVGscissor& scissor, 95 | nytl::Span verts); 96 | 97 | /// Start a new frame. Sets the viewport parameters. 98 | /// Effectively resets all stored draw commands. 99 | /// Will invalidate all commandBuffers that were recorded before. 100 | void start(unsigned int width, unsigned int height); 101 | 102 | /// Cancel the current frame. 103 | void cancel(); 104 | 105 | /// Flushs the current frame, i.e. renders it on the render target. 106 | /// This call will block until the device has finished its commands. 107 | void flush(); 108 | 109 | /// Records all given draw commands since the last start frame call to the given 110 | /// command buffer. Note that the caller must assure that the commandBuffer is in a valid state 111 | /// for this Renderer to record its commands (i.e. recording state, matching renderPass). 112 | /// All commandBuffers will remain valid until the next draw (fill/stroie/triangles) call 113 | /// or until start is called. 114 | void record(vk::CommandBuffer cmdBuffer); 115 | 116 | /// Creates a texture for the given parameters and returns its id. 117 | unsigned int createTexture(vk::Format format, unsigned int width, unsigned int height, 118 | const std::uint8_t* data = nullptr); 119 | 120 | /// Deletes the texture with the given id. 121 | /// If the given id could not be found returns false. 122 | bool deleteTexture(unsigned int id); 123 | 124 | const vpp::Sampler& sampler() const { return sampler_; } 125 | const vpp::RenderPass& renderPass() const { return renderPass_; } 126 | const vpp::Buffer& uniformBuffer() const { return uniformBuffer_; } 127 | const vpp::Buffer& vertexBuffer() const { return vertexBuffer_; } 128 | const vpp::DescriptorPool& descriptorPool() const { return descriptorPool_; } 129 | const vpp::DescriptorSetLayout& descriptorLayout() const { return descriptorLayout_; } 130 | const vpp::PipelineLayout& pipelineLayout() const { return pipelineLayout_; } 131 | 132 | const vpp::Swapchain* swapchain() const { return swapchain_; } 133 | const vpp::SwapchainRenderer& renderer() const { return renderer_; } 134 | 135 | const vpp::Framebuffer* framebuffer() const { return framebuffer_; } 136 | const vpp::CommandBuffer& commandBuffer() const { return commandBuffer_; } 137 | vk::RenderPass vkRenderPass() const 138 | { return swapchain_ ? renderPass_ : renderPassHandle_; } 139 | 140 | protected: 141 | void init(); 142 | void initRenderPass(const vpp::Device& dev, vk::Format attachment); 143 | 144 | //for the c implementation 145 | Renderer& operator=(Renderer&& other) = default; 146 | 147 | DrawData& parsePaint(const NVGpaint& paint, const NVGscissor& scissor, float fringe, 148 | float strokeWidth); 149 | 150 | protected: 151 | const vpp::Swapchain* swapchain_ = nullptr; // if rendering on swapchain 152 | vpp::SwapchainRenderer renderer_; // used if rendering on swapchain 153 | vpp::RenderPass renderPass_; // for swapchain 154 | 155 | const vpp::Framebuffer* framebuffer_ = nullptr; // if rendering into framebuffer 156 | vpp::CommandBuffer commandBuffer_; // commandBuffer to submit if rendering into fb 157 | const vpp::Queue* renderQueue_; // queue used for rendering if rendering into fb 158 | const vpp::Queue* presentQueue_; // queue for presenting 159 | vk::RenderPass renderPassHandle_; // for framebuffer 160 | 161 | unsigned int texID_ = 0; // the currently highest texture id 162 | std::vector textures_; 163 | 164 | vpp::Buffer uniformBuffer_; 165 | vpp::Buffer vertexBuffer_; 166 | 167 | std::vector drawDatas_; 168 | std::vector vertices_; 169 | 170 | unsigned int width_ {}; 171 | unsigned int height_ {}; 172 | 173 | vpp::Sampler sampler_; 174 | 175 | vpp::DescriptorPool descriptorPool_; 176 | vpp::DescriptorSetLayout descriptorLayout_; 177 | unsigned int descriptorPoolSize_ {}; // current maximal draw calls count 178 | 179 | vpp::PipelineLayout pipelineLayout_; 180 | vpp::Pipeline fanPipeline_; 181 | vpp::Pipeline stripPipeline_; 182 | vpp::Pipeline listPipeline_; 183 | unsigned int bound_ = 0; 184 | 185 | Texture dummyTexture_; 186 | 187 | // settings 188 | bool edgeAA_ = false; 189 | }; 190 | 191 | /// Creates the nanovg context for the previoiusly created renderer object. 192 | /// Note that this constructor can be useful if one wants to keep a reference to the underlaying 193 | /// Renderer object. 194 | NVGcontext* createContext(std::unique_ptr renderer); 195 | 196 | /// Creates the nanovg context for a given Swapchain. 197 | NVGcontext* createContext(const vpp::Swapchain& swapchain); 198 | 199 | /// Creates the nanovg context for a given Framebuffer and RenderPass. 200 | NVGcontext* createContext(const vpp::Framebuffer& fb, vk::RenderPass rp); 201 | 202 | /// Destroys a nanovg context that was created by this library. 203 | /// Note that passing a nanovg context that was not created by this library results in undefined 204 | /// behaviour. 205 | void destroyContext(const NVGcontext& context); 206 | 207 | /// Returns the underlaying renderer object from a nanovg context. 208 | /// Note that passing a nanovg context that was not created by this library results in undefined 209 | /// behaviour. 210 | const Renderer& getRenderer(const NVGcontext& context); 211 | Renderer& getRenderer(NVGcontext& context); 212 | 213 | } // namespace vvg 214 | 215 | #endif // header guard 216 | 217 | //TODO: implement renderer constructor for framebuffer 218 | 219 | // Copyright © 2016 nyorain 220 | // 221 | // Permission is hereby granted, free of charge, to any person 222 | // obtaining a copy of this software and associated documentation 223 | // files (the “Software”), to deal in the Software without 224 | // restriction, including without limitation the rights to use, 225 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 226 | // copies of the Software, and to permit persons to whom the 227 | // Software is furnished to do so, subject to the following 228 | // conditions: 229 | // 230 | // The above copyright notice and this permission notice shall be 231 | // included in all copies or substantial portions of the Software. 232 | // 233 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 234 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 235 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 236 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 237 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 238 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 239 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 240 | // OTHER DEALINGS IN THE SOFTWARE. 241 | -------------------------------------------------------------------------------- /src/nanovg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #ifndef NANOVG_H 20 | #define NANOVG_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define NVG_PI 3.14159265358979323846264338327f 27 | 28 | #ifdef _MSC_VER 29 | #pragma warning(push) 30 | #pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union 31 | #endif 32 | 33 | typedef struct NVGcontext NVGcontext; 34 | 35 | struct NVGcolor { 36 | union { 37 | float rgba[4]; 38 | struct { 39 | float r,g,b,a; 40 | }; 41 | }; 42 | }; 43 | typedef struct NVGcolor NVGcolor; 44 | 45 | struct NVGpaint { 46 | float xform[6]; 47 | float extent[2]; 48 | float radius; 49 | float feather; 50 | NVGcolor innerColor; 51 | NVGcolor outerColor; 52 | int image; 53 | }; 54 | typedef struct NVGpaint NVGpaint; 55 | 56 | enum NVGwinding { 57 | NVG_CCW = 1, // Winding for solid shapes 58 | NVG_CW = 2, // Winding for holes 59 | }; 60 | 61 | enum NVGsolidity { 62 | NVG_SOLID = 1, // CCW 63 | NVG_HOLE = 2, // CW 64 | }; 65 | 66 | enum NVGlineCap { 67 | NVG_BUTT, 68 | NVG_ROUND, 69 | NVG_SQUARE, 70 | NVG_BEVEL, 71 | NVG_MITER, 72 | }; 73 | 74 | enum NVGalign { 75 | // Horizontal align 76 | NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left. 77 | NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center. 78 | NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right. 79 | // Vertical align 80 | NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. 81 | NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. 82 | NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. 83 | NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. 84 | }; 85 | 86 | struct NVGglyphPosition { 87 | const char* str; // Position of the glyph in the input string. 88 | float x; // The x-coordinate of the logical glyph position. 89 | float minx, maxx; // The bounds of the glyph shape. 90 | }; 91 | typedef struct NVGglyphPosition NVGglyphPosition; 92 | 93 | struct NVGtextRow { 94 | const char* start; // Pointer to the input text where the row starts. 95 | const char* end; // Pointer to the input text where the row ends (one past the last character). 96 | const char* next; // Pointer to the beginning of the next row. 97 | float width; // Logical width of the row. 98 | float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. 99 | }; 100 | typedef struct NVGtextRow NVGtextRow; 101 | 102 | enum NVGimageFlags { 103 | NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. 104 | NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. 105 | NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. 106 | NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. 107 | NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. 108 | }; 109 | 110 | // Begin drawing a new frame 111 | // Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() 112 | // nvgBeginFrame() defines the size of the window to render to in relation currently 113 | // set viewport (i.e. glViewport on GL backends). Device pixel ration allows to 114 | // control the rendering on Hi-DPI devices. 115 | // For example, GLFW returns two dimension for an opened window: window size and 116 | // frame buffer size. In that case you would set windowWidth/Height to the window size 117 | // devicePixelRatio to: frameBufferWidth / windowWidth. 118 | void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio); 119 | 120 | // Cancels drawing the current frame. 121 | void nvgCancelFrame(NVGcontext* ctx); 122 | 123 | // Ends drawing flushing remaining render state. 124 | void nvgEndFrame(NVGcontext* ctx); 125 | 126 | // 127 | // Color utils 128 | // 129 | // Colors in NanoVG are stored as unsigned ints in ABGR format. 130 | 131 | // Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f). 132 | NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b); 133 | 134 | // Returns a color value from red, green, blue values. Alpha will be set to 1.0f. 135 | NVGcolor nvgRGBf(float r, float g, float b); 136 | 137 | 138 | // Returns a color value from red, green, blue and alpha values. 139 | NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); 140 | 141 | // Returns a color value from red, green, blue and alpha values. 142 | NVGcolor nvgRGBAf(float r, float g, float b, float a); 143 | 144 | 145 | // Linearly interpolates from color c0 to c1, and returns resulting color value. 146 | NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u); 147 | 148 | // Sets transparency of a color value. 149 | NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a); 150 | 151 | // Sets transparency of a color value. 152 | NVGcolor nvgTransRGBAf(NVGcolor c0, float a); 153 | 154 | // Returns color value specified by hue, saturation and lightness. 155 | // HSL values are all in range [0..1], alpha will be set to 255. 156 | NVGcolor nvgHSL(float h, float s, float l); 157 | 158 | // Returns color value specified by hue, saturation and lightness and alpha. 159 | // HSL values are all in range [0..1], alpha in range [0..255] 160 | NVGcolor nvgHSLA(float h, float s, float l, unsigned char a); 161 | 162 | // 163 | // State Handling 164 | // 165 | // NanoVG contains state which represents how paths will be rendered. 166 | // The state contains transform, fill and stroke styles, text and font styles, 167 | // and scissor clipping. 168 | 169 | // Pushes and saves the current render state into a state stack. 170 | // A matching nvgRestore() must be used to restore the state. 171 | void nvgSave(NVGcontext* ctx); 172 | 173 | // Pops and restores current render state. 174 | void nvgRestore(NVGcontext* ctx); 175 | 176 | // Resets current render state to default values. Does not affect the render state stack. 177 | void nvgReset(NVGcontext* ctx); 178 | 179 | // 180 | // Render styles 181 | // 182 | // Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern. 183 | // Solid color is simply defined as a color value, different kinds of paints can be created 184 | // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). 185 | // 186 | // Current render style can be saved and restored using nvgSave() and nvgRestore(). 187 | 188 | // Sets current stroke style to a solid color. 189 | void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); 190 | 191 | // Sets current stroke style to a paint, which can be a one of the gradients or a pattern. 192 | void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint); 193 | 194 | // Sets current fill style to a solid color. 195 | void nvgFillColor(NVGcontext* ctx, NVGcolor color); 196 | 197 | // Sets current fill style to a paint, which can be a one of the gradients or a pattern. 198 | void nvgFillPaint(NVGcontext* ctx, NVGpaint paint); 199 | 200 | // Sets the miter limit of the stroke style. 201 | // Miter limit controls when a sharp corner is beveled. 202 | void nvgMiterLimit(NVGcontext* ctx, float limit); 203 | 204 | // Sets the stroke width of the stroke style. 205 | void nvgStrokeWidth(NVGcontext* ctx, float size); 206 | 207 | // Sets how the end of the line (cap) is drawn, 208 | // Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. 209 | void nvgLineCap(NVGcontext* ctx, int cap); 210 | 211 | // Sets how sharp path corners are drawn. 212 | // Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL. 213 | void nvgLineJoin(NVGcontext* ctx, int join); 214 | 215 | // Sets the transparency applied to all rendered shapes. 216 | // Already transparent paths will get proportionally more transparent as well. 217 | void nvgGlobalAlpha(NVGcontext* ctx, float alpha); 218 | 219 | // 220 | // Transforms 221 | // 222 | // The paths, gradients, patterns and scissor region are transformed by an transformation 223 | // matrix at the time when they are passed to the API. 224 | // The current transformation matrix is a affine matrix: 225 | // [sx kx tx] 226 | // [ky sy ty] 227 | // [ 0 0 1] 228 | // Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. 229 | // The last row is assumed to be 0,0,1 and is not stored. 230 | // 231 | // Apart from nvgResetTransform(), each transformation function first creates 232 | // specific transformation matrix and pre-multiplies the current transformation by it. 233 | // 234 | // Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). 235 | 236 | // Resets current transform to a identity matrix. 237 | void nvgResetTransform(NVGcontext* ctx); 238 | 239 | // Premultiplies current coordinate system by specified matrix. 240 | // The parameters are interpreted as matrix as follows: 241 | // [a c e] 242 | // [b d f] 243 | // [0 0 1] 244 | void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f); 245 | 246 | // Translates current coordinate system. 247 | void nvgTranslate(NVGcontext* ctx, float x, float y); 248 | 249 | // Rotates current coordinate system. Angle is specified in radians. 250 | void nvgRotate(NVGcontext* ctx, float angle); 251 | 252 | // Skews the current coordinate system along X axis. Angle is specified in radians. 253 | void nvgSkewX(NVGcontext* ctx, float angle); 254 | 255 | // Skews the current coordinate system along Y axis. Angle is specified in radians. 256 | void nvgSkewY(NVGcontext* ctx, float angle); 257 | 258 | // Scales the current coordinate system. 259 | void nvgScale(NVGcontext* ctx, float x, float y); 260 | 261 | // Stores the top part (a-f) of the current transformation matrix in to the specified buffer. 262 | // [a c e] 263 | // [b d f] 264 | // [0 0 1] 265 | // There should be space for 6 floats in the return buffer for the values a-f. 266 | void nvgCurrentTransform(NVGcontext* ctx, float* xform); 267 | 268 | 269 | // The following functions can be used to make calculations on 2x3 transformation matrices. 270 | // A 2x3 matrix is represented as float[6]. 271 | 272 | // Sets the transform to identity matrix. 273 | void nvgTransformIdentity(float* dst); 274 | 275 | // Sets the transform to translation matrix matrix. 276 | void nvgTransformTranslate(float* dst, float tx, float ty); 277 | 278 | // Sets the transform to scale matrix. 279 | void nvgTransformScale(float* dst, float sx, float sy); 280 | 281 | // Sets the transform to rotate matrix. Angle is specified in radians. 282 | void nvgTransformRotate(float* dst, float a); 283 | 284 | // Sets the transform to skew-x matrix. Angle is specified in radians. 285 | void nvgTransformSkewX(float* dst, float a); 286 | 287 | // Sets the transform to skew-y matrix. Angle is specified in radians. 288 | void nvgTransformSkewY(float* dst, float a); 289 | 290 | // Sets the transform to the result of multiplication of two transforms, of A = A*B. 291 | void nvgTransformMultiply(float* dst, const float* src); 292 | 293 | // Sets the transform to the result of multiplication of two transforms, of A = B*A. 294 | void nvgTransformPremultiply(float* dst, const float* src); 295 | 296 | // Sets the destination to inverse of specified transform. 297 | // Returns 1 if the inverse could be calculated, else 0. 298 | int nvgTransformInverse(float* dst, const float* src); 299 | 300 | // Transform a point by given transform. 301 | void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy); 302 | 303 | // Converts degrees to radians and vice versa. 304 | float nvgDegToRad(float deg); 305 | float nvgRadToDeg(float rad); 306 | 307 | // 308 | // Images 309 | // 310 | // NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. 311 | // In addition you can upload your own image. The image loading is provided by stb_image. 312 | // The parameter imageFlags is combination of flags defined in NVGimageFlags. 313 | 314 | // Creates image by loading it from the disk from specified file name. 315 | // Returns handle to the image. 316 | int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); 317 | 318 | // Creates image by loading it from the specified chunk of memory. 319 | // Returns handle to the image. 320 | int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); 321 | 322 | // Creates image from specified image data. 323 | // Returns handle to the image. 324 | int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data); 325 | 326 | // Updates image data specified by image handle. 327 | void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data); 328 | 329 | // Returns the dimensions of a created image. 330 | void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h); 331 | 332 | // Deletes created image. 333 | void nvgDeleteImage(NVGcontext* ctx, int image); 334 | 335 | // 336 | // Paints 337 | // 338 | // NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. 339 | // These can be used as paints for strokes and fills. 340 | 341 | // Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates 342 | // of the linear gradient, icol specifies the start color and ocol the end color. 343 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 344 | NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey, 345 | NVGcolor icol, NVGcolor ocol); 346 | 347 | // Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering 348 | // drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, 349 | // (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry 350 | // the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. 351 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 352 | NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, 353 | float r, float f, NVGcolor icol, NVGcolor ocol); 354 | 355 | // Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify 356 | // the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. 357 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 358 | NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, 359 | NVGcolor icol, NVGcolor ocol); 360 | 361 | // Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern, 362 | // (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. 363 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 364 | NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, 365 | float angle, int image, float alpha); 366 | 367 | // 368 | // Scissoring 369 | // 370 | // Scissoring allows you to clip the rendering into a rectangle. This is useful for various 371 | // user interface cases like rendering a text edit or a timeline. 372 | 373 | // Sets the current scissor rectangle. 374 | // The scissor rectangle is transformed by the current transform. 375 | void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h); 376 | 377 | // Intersects current scissor rectangle with the specified rectangle. 378 | // The scissor rectangle is transformed by the current transform. 379 | // Note: in case the rotation of previous scissor rect differs from 380 | // the current one, the intersection will be done between the specified 381 | // rectangle and the previous scissor rectangle transformed in the current 382 | // transform space. The resulting shape is always rectangle. 383 | void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h); 384 | 385 | // Reset and disables scissoring. 386 | void nvgResetScissor(NVGcontext* ctx); 387 | 388 | // 389 | // Paths 390 | // 391 | // Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths. 392 | // Then you define one or more paths and sub-paths which describe the shape. The are functions 393 | // to draw common shapes like rectangles and circles, and lower level step-by-step functions, 394 | // which allow to define a path curve by curve. 395 | // 396 | // NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise 397 | // winding and holes should have counter clockwise order. To specify winding of a path you can 398 | // call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW. 399 | // 400 | // Finally you can fill the path using current fill style by calling nvgFill(), and stroke it 401 | // with current stroke style by calling nvgStroke(). 402 | // 403 | // The curve segments and sub-paths are transformed by the current transform. 404 | 405 | // Clears the current path and sub-paths. 406 | void nvgBeginPath(NVGcontext* ctx); 407 | 408 | // Starts new sub-path with specified point as first point. 409 | void nvgMoveTo(NVGcontext* ctx, float x, float y); 410 | 411 | // Adds line segment from the last point in the path to the specified point. 412 | void nvgLineTo(NVGcontext* ctx, float x, float y); 413 | 414 | // Adds cubic bezier segment from last point in the path via two control points to the specified point. 415 | void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y); 416 | 417 | // Adds quadratic bezier segment from last point in the path via a control point to the specified point. 418 | void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y); 419 | 420 | // Adds an arc segment at the corner defined by the last path point, and two specified points. 421 | void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius); 422 | 423 | // Closes current sub-path with a line segment. 424 | void nvgClosePath(NVGcontext* ctx); 425 | 426 | // Sets the current sub-path winding, see NVGwinding and NVGsolidity. 427 | void nvgPathWinding(NVGcontext* ctx, int dir); 428 | 429 | // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, 430 | // and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW). 431 | // Angles are specified in radians. 432 | void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir); 433 | 434 | // Creates new rectangle shaped sub-path. 435 | void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); 436 | 437 | // Creates new rounded rectangle shaped sub-path. 438 | void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); 439 | 440 | // Creates new ellipse shaped sub-path. 441 | void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); 442 | 443 | // Creates new circle shaped sub-path. 444 | void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); 445 | 446 | // Fills the current path with current fill style. 447 | void nvgFill(NVGcontext* ctx); 448 | 449 | // Fills the current path with current stroke style. 450 | void nvgStroke(NVGcontext* ctx); 451 | 452 | 453 | // 454 | // Text 455 | // 456 | // NanoVG allows you to load .ttf files and use the font to render text. 457 | // 458 | // The appearance of the text can be defined by setting the current text style 459 | // and by specifying the fill color. Common text and font settings such as 460 | // font size, letter spacing and text align are supported. Font blur allows you 461 | // to create simple text effects such as drop shadows. 462 | // 463 | // At render time the font face can be set based on the font handles or name. 464 | // 465 | // Font measure functions return values in local space, the calculations are 466 | // carried in the same resolution as the final rendering. This is done because 467 | // the text glyph positions are snapped to the nearest pixels sharp rendering. 468 | // 469 | // The local space means that values are not rotated or scale as per the current 470 | // transformation. For example if you set font size to 12, which would mean that 471 | // line height is 16, then regardless of the current scaling and rotation, the 472 | // returned line height is always 16. Some measures may vary because of the scaling 473 | // since aforementioned pixel snapping. 474 | // 475 | // While this may sound a little odd, the setup allows you to always render the 476 | // same way regardless of scaling. I.e. following works regardless of scaling: 477 | // 478 | // const char* txt = "Text me up."; 479 | // nvgTextBounds(vg, x,y, txt, NULL, bounds); 480 | // nvgBeginPath(vg); 481 | // nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); 482 | // nvgFill(vg); 483 | // 484 | // Note: currently only solid color fill is supported for text. 485 | 486 | // Creates font by loading it from the disk from specified file name. 487 | // Returns handle to the font. 488 | int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); 489 | 490 | // Creates font by loading it from the specified memory chunk. 491 | // Returns handle to the font. 492 | int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); 493 | 494 | // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. 495 | int nvgFindFont(NVGcontext* ctx, const char* name); 496 | 497 | // Sets the font size of current text style. 498 | void nvgFontSize(NVGcontext* ctx, float size); 499 | 500 | // Sets the blur of current text style. 501 | void nvgFontBlur(NVGcontext* ctx, float blur); 502 | 503 | // Sets the letter spacing of current text style. 504 | void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); 505 | 506 | // Sets the proportional line height of current text style. The line height is specified as multiple of font size. 507 | void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); 508 | 509 | // Sets the text align of current text style, see NVGalign for options. 510 | void nvgTextAlign(NVGcontext* ctx, int align); 511 | 512 | // Sets the font face based on specified id of current text style. 513 | void nvgFontFaceId(NVGcontext* ctx, int font); 514 | 515 | // Sets the font face based on specified name of current text style. 516 | void nvgFontFace(NVGcontext* ctx, const char* font); 517 | 518 | // Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. 519 | float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end); 520 | 521 | // Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. 522 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 523 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 524 | void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end); 525 | 526 | // Measures the specified text string. Parameter bounds should be a pointer to float[4], 527 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 528 | // Returns the horizontal advance of the measured text (i.e. where the next character should drawn). 529 | // Measured values are returned in local coordinate space. 530 | float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds); 531 | 532 | // Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], 533 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 534 | // Measured values are returned in local coordinate space. 535 | void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds); 536 | 537 | // Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. 538 | // Measured values are returned in local coordinate space. 539 | int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions); 540 | 541 | // Returns the vertical metrics based on the current text style. 542 | // Measured values are returned in local coordinate space. 543 | void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh); 544 | 545 | // Breaks the specified text into lines. If end is specified only the sub-string will be used. 546 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 547 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 548 | int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows); 549 | 550 | // 551 | // Internal Render API 552 | // 553 | enum NVGtexture { 554 | NVG_TEXTURE_ALPHA = 0x01, 555 | NVG_TEXTURE_RGBA = 0x02, 556 | }; 557 | 558 | struct NVGscissor { 559 | float xform[6]; 560 | float extent[2]; 561 | }; 562 | typedef struct NVGscissor NVGscissor; 563 | 564 | struct NVGvertex { 565 | float x,y,u,v; 566 | }; 567 | typedef struct NVGvertex NVGvertex; 568 | 569 | struct NVGpath { 570 | int first; 571 | int count; 572 | unsigned char closed; 573 | int nbevel; 574 | NVGvertex* fill; 575 | int nfill; 576 | NVGvertex* stroke; 577 | int nstroke; 578 | int winding; 579 | int convex; 580 | }; 581 | typedef struct NVGpath NVGpath; 582 | 583 | struct NVGparams { 584 | void* userPtr; 585 | int edgeAntiAlias; 586 | int (*renderCreate)(void* uptr); 587 | int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); 588 | int (*renderDeleteTexture)(void* uptr, int image); 589 | int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); 590 | int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); 591 | void (*renderViewport)(void* uptr, int width, int height); 592 | void (*renderCancel)(void* uptr); 593 | void (*renderFlush)(void* uptr); 594 | void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); 595 | void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); 596 | void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts); 597 | void (*renderDelete)(void* uptr); 598 | }; 599 | typedef struct NVGparams NVGparams; 600 | 601 | // Constructor and destructor, called by the render back-end. 602 | NVGcontext* nvgCreateInternal(NVGparams* params); 603 | void nvgDeleteInternal(NVGcontext* ctx); 604 | 605 | NVGparams* nvgInternalParams(NVGcontext* ctx); 606 | 607 | // Debug function to dump cached path data. 608 | void nvgDebugDumpPathCache(NVGcontext* ctx); 609 | 610 | #ifdef _MSC_VER 611 | #pragma warning(pop) 612 | #endif 613 | 614 | #define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; } 615 | 616 | #ifdef __cplusplus 617 | } 618 | #endif 619 | 620 | #endif // NANOVG_H 621 | -------------------------------------------------------------------------------- /examples/nanovg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #ifndef NANOVG_H 20 | #define NANOVG_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define NVG_PI 3.14159265358979323846264338327f 27 | 28 | #ifdef _MSC_VER 29 | #pragma warning(push) 30 | #pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union 31 | #endif 32 | 33 | typedef struct NVGcontext NVGcontext; 34 | 35 | struct NVGcolor { 36 | union { 37 | float rgba[4]; 38 | struct { 39 | float r,g,b,a; 40 | }; 41 | }; 42 | }; 43 | typedef struct NVGcolor NVGcolor; 44 | 45 | struct NVGpaint { 46 | float xform[6]; 47 | float extent[2]; 48 | float radius; 49 | float feather; 50 | NVGcolor innerColor; 51 | NVGcolor outerColor; 52 | int image; 53 | }; 54 | typedef struct NVGpaint NVGpaint; 55 | 56 | enum NVGwinding { 57 | NVG_CCW = 1, // Winding for solid shapes 58 | NVG_CW = 2, // Winding for holes 59 | }; 60 | 61 | enum NVGsolidity { 62 | NVG_SOLID = 1, // CCW 63 | NVG_HOLE = 2, // CW 64 | }; 65 | 66 | enum NVGlineCap { 67 | NVG_BUTT, 68 | NVG_ROUND, 69 | NVG_SQUARE, 70 | NVG_BEVEL, 71 | NVG_MITER, 72 | }; 73 | 74 | enum NVGalign { 75 | // Horizontal align 76 | NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left. 77 | NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center. 78 | NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right. 79 | // Vertical align 80 | NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. 81 | NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. 82 | NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. 83 | NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. 84 | }; 85 | 86 | struct NVGglyphPosition { 87 | const char* str; // Position of the glyph in the input string. 88 | float x; // The x-coordinate of the logical glyph position. 89 | float minx, maxx; // The bounds of the glyph shape. 90 | }; 91 | typedef struct NVGglyphPosition NVGglyphPosition; 92 | 93 | struct NVGtextRow { 94 | const char* start; // Pointer to the input text where the row starts. 95 | const char* end; // Pointer to the input text where the row ends (one past the last character). 96 | const char* next; // Pointer to the beginning of the next row. 97 | float width; // Logical width of the row. 98 | float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. 99 | }; 100 | typedef struct NVGtextRow NVGtextRow; 101 | 102 | enum NVGimageFlags { 103 | NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. 104 | NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. 105 | NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. 106 | NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. 107 | NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. 108 | }; 109 | 110 | // Begin drawing a new frame 111 | // Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() 112 | // nvgBeginFrame() defines the size of the window to render to in relation currently 113 | // set viewport (i.e. glViewport on GL backends). Device pixel ration allows to 114 | // control the rendering on Hi-DPI devices. 115 | // For example, GLFW returns two dimension for an opened window: window size and 116 | // frame buffer size. In that case you would set windowWidth/Height to the window size 117 | // devicePixelRatio to: frameBufferWidth / windowWidth. 118 | void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio); 119 | 120 | // Cancels drawing the current frame. 121 | void nvgCancelFrame(NVGcontext* ctx); 122 | 123 | // Ends drawing flushing remaining render state. 124 | void nvgEndFrame(NVGcontext* ctx); 125 | 126 | // 127 | // Color utils 128 | // 129 | // Colors in NanoVG are stored as unsigned ints in ABGR format. 130 | 131 | // Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f). 132 | NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b); 133 | 134 | // Returns a color value from red, green, blue values. Alpha will be set to 1.0f. 135 | NVGcolor nvgRGBf(float r, float g, float b); 136 | 137 | 138 | // Returns a color value from red, green, blue and alpha values. 139 | NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); 140 | 141 | // Returns a color value from red, green, blue and alpha values. 142 | NVGcolor nvgRGBAf(float r, float g, float b, float a); 143 | 144 | 145 | // Linearly interpolates from color c0 to c1, and returns resulting color value. 146 | NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u); 147 | 148 | // Sets transparency of a color value. 149 | NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a); 150 | 151 | // Sets transparency of a color value. 152 | NVGcolor nvgTransRGBAf(NVGcolor c0, float a); 153 | 154 | // Returns color value specified by hue, saturation and lightness. 155 | // HSL values are all in range [0..1], alpha will be set to 255. 156 | NVGcolor nvgHSL(float h, float s, float l); 157 | 158 | // Returns color value specified by hue, saturation and lightness and alpha. 159 | // HSL values are all in range [0..1], alpha in range [0..255] 160 | NVGcolor nvgHSLA(float h, float s, float l, unsigned char a); 161 | 162 | // 163 | // State Handling 164 | // 165 | // NanoVG contains state which represents how paths will be rendered. 166 | // The state contains transform, fill and stroke styles, text and font styles, 167 | // and scissor clipping. 168 | 169 | // Pushes and saves the current render state into a state stack. 170 | // A matching nvgRestore() must be used to restore the state. 171 | void nvgSave(NVGcontext* ctx); 172 | 173 | // Pops and restores current render state. 174 | void nvgRestore(NVGcontext* ctx); 175 | 176 | // Resets current render state to default values. Does not affect the render state stack. 177 | void nvgReset(NVGcontext* ctx); 178 | 179 | // 180 | // Render styles 181 | // 182 | // Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern. 183 | // Solid color is simply defined as a color value, different kinds of paints can be created 184 | // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). 185 | // 186 | // Current render style can be saved and restored using nvgSave() and nvgRestore(). 187 | 188 | // Sets current stroke style to a solid color. 189 | void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); 190 | 191 | // Sets current stroke style to a paint, which can be a one of the gradients or a pattern. 192 | void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint); 193 | 194 | // Sets current fill style to a solid color. 195 | void nvgFillColor(NVGcontext* ctx, NVGcolor color); 196 | 197 | // Sets current fill style to a paint, which can be a one of the gradients or a pattern. 198 | void nvgFillPaint(NVGcontext* ctx, NVGpaint paint); 199 | 200 | // Sets the miter limit of the stroke style. 201 | // Miter limit controls when a sharp corner is beveled. 202 | void nvgMiterLimit(NVGcontext* ctx, float limit); 203 | 204 | // Sets the stroke width of the stroke style. 205 | void nvgStrokeWidth(NVGcontext* ctx, float size); 206 | 207 | // Sets how the end of the line (cap) is drawn, 208 | // Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. 209 | void nvgLineCap(NVGcontext* ctx, int cap); 210 | 211 | // Sets how sharp path corners are drawn. 212 | // Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL. 213 | void nvgLineJoin(NVGcontext* ctx, int join); 214 | 215 | // Sets the transparency applied to all rendered shapes. 216 | // Already transparent paths will get proportionally more transparent as well. 217 | void nvgGlobalAlpha(NVGcontext* ctx, float alpha); 218 | 219 | // 220 | // Transforms 221 | // 222 | // The paths, gradients, patterns and scissor region are transformed by an transformation 223 | // matrix at the time when they are passed to the API. 224 | // The current transformation matrix is a affine matrix: 225 | // [sx kx tx] 226 | // [ky sy ty] 227 | // [ 0 0 1] 228 | // Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. 229 | // The last row is assumed to be 0,0,1 and is not stored. 230 | // 231 | // Apart from nvgResetTransform(), each transformation function first creates 232 | // specific transformation matrix and pre-multiplies the current transformation by it. 233 | // 234 | // Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). 235 | 236 | // Resets current transform to a identity matrix. 237 | void nvgResetTransform(NVGcontext* ctx); 238 | 239 | // Premultiplies current coordinate system by specified matrix. 240 | // The parameters are interpreted as matrix as follows: 241 | // [a c e] 242 | // [b d f] 243 | // [0 0 1] 244 | void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f); 245 | 246 | // Translates current coordinate system. 247 | void nvgTranslate(NVGcontext* ctx, float x, float y); 248 | 249 | // Rotates current coordinate system. Angle is specified in radians. 250 | void nvgRotate(NVGcontext* ctx, float angle); 251 | 252 | // Skews the current coordinate system along X axis. Angle is specified in radians. 253 | void nvgSkewX(NVGcontext* ctx, float angle); 254 | 255 | // Skews the current coordinate system along Y axis. Angle is specified in radians. 256 | void nvgSkewY(NVGcontext* ctx, float angle); 257 | 258 | // Scales the current coordinate system. 259 | void nvgScale(NVGcontext* ctx, float x, float y); 260 | 261 | // Stores the top part (a-f) of the current transformation matrix in to the specified buffer. 262 | // [a c e] 263 | // [b d f] 264 | // [0 0 1] 265 | // There should be space for 6 floats in the return buffer for the values a-f. 266 | void nvgCurrentTransform(NVGcontext* ctx, float* xform); 267 | 268 | 269 | // The following functions can be used to make calculations on 2x3 transformation matrices. 270 | // A 2x3 matrix is represented as float[6]. 271 | 272 | // Sets the transform to identity matrix. 273 | void nvgTransformIdentity(float* dst); 274 | 275 | // Sets the transform to translation matrix matrix. 276 | void nvgTransformTranslate(float* dst, float tx, float ty); 277 | 278 | // Sets the transform to scale matrix. 279 | void nvgTransformScale(float* dst, float sx, float sy); 280 | 281 | // Sets the transform to rotate matrix. Angle is specified in radians. 282 | void nvgTransformRotate(float* dst, float a); 283 | 284 | // Sets the transform to skew-x matrix. Angle is specified in radians. 285 | void nvgTransformSkewX(float* dst, float a); 286 | 287 | // Sets the transform to skew-y matrix. Angle is specified in radians. 288 | void nvgTransformSkewY(float* dst, float a); 289 | 290 | // Sets the transform to the result of multiplication of two transforms, of A = A*B. 291 | void nvgTransformMultiply(float* dst, const float* src); 292 | 293 | // Sets the transform to the result of multiplication of two transforms, of A = B*A. 294 | void nvgTransformPremultiply(float* dst, const float* src); 295 | 296 | // Sets the destination to inverse of specified transform. 297 | // Returns 1 if the inverse could be calculated, else 0. 298 | int nvgTransformInverse(float* dst, const float* src); 299 | 300 | // Transform a point by given transform. 301 | void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy); 302 | 303 | // Converts degrees to radians and vice versa. 304 | float nvgDegToRad(float deg); 305 | float nvgRadToDeg(float rad); 306 | 307 | // 308 | // Images 309 | // 310 | // NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. 311 | // In addition you can upload your own image. The image loading is provided by stb_image. 312 | // The parameter imageFlags is combination of flags defined in NVGimageFlags. 313 | 314 | // Creates image by loading it from the disk from specified file name. 315 | // Returns handle to the image. 316 | int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); 317 | 318 | // Creates image by loading it from the specified chunk of memory. 319 | // Returns handle to the image. 320 | int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); 321 | 322 | // Creates image from specified image data. 323 | // Returns handle to the image. 324 | int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data); 325 | 326 | // Updates image data specified by image handle. 327 | void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data); 328 | 329 | // Returns the dimensions of a created image. 330 | void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h); 331 | 332 | // Deletes created image. 333 | void nvgDeleteImage(NVGcontext* ctx, int image); 334 | 335 | // 336 | // Paints 337 | // 338 | // NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. 339 | // These can be used as paints for strokes and fills. 340 | 341 | // Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates 342 | // of the linear gradient, icol specifies the start color and ocol the end color. 343 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 344 | NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey, 345 | NVGcolor icol, NVGcolor ocol); 346 | 347 | // Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering 348 | // drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, 349 | // (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry 350 | // the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. 351 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 352 | NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, 353 | float r, float f, NVGcolor icol, NVGcolor ocol); 354 | 355 | // Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify 356 | // the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. 357 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 358 | NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, 359 | NVGcolor icol, NVGcolor ocol); 360 | 361 | // Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern, 362 | // (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. 363 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 364 | NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, 365 | float angle, int image, float alpha); 366 | 367 | // 368 | // Scissoring 369 | // 370 | // Scissoring allows you to clip the rendering into a rectangle. This is useful for various 371 | // user interface cases like rendering a text edit or a timeline. 372 | 373 | // Sets the current scissor rectangle. 374 | // The scissor rectangle is transformed by the current transform. 375 | void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h); 376 | 377 | // Intersects current scissor rectangle with the specified rectangle. 378 | // The scissor rectangle is transformed by the current transform. 379 | // Note: in case the rotation of previous scissor rect differs from 380 | // the current one, the intersection will be done between the specified 381 | // rectangle and the previous scissor rectangle transformed in the current 382 | // transform space. The resulting shape is always rectangle. 383 | void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h); 384 | 385 | // Reset and disables scissoring. 386 | void nvgResetScissor(NVGcontext* ctx); 387 | 388 | // 389 | // Paths 390 | // 391 | // Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths. 392 | // Then you define one or more paths and sub-paths which describe the shape. The are functions 393 | // to draw common shapes like rectangles and circles, and lower level step-by-step functions, 394 | // which allow to define a path curve by curve. 395 | // 396 | // NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise 397 | // winding and holes should have counter clockwise order. To specify winding of a path you can 398 | // call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW. 399 | // 400 | // Finally you can fill the path using current fill style by calling nvgFill(), and stroke it 401 | // with current stroke style by calling nvgStroke(). 402 | // 403 | // The curve segments and sub-paths are transformed by the current transform. 404 | 405 | // Clears the current path and sub-paths. 406 | void nvgBeginPath(NVGcontext* ctx); 407 | 408 | // Starts new sub-path with specified point as first point. 409 | void nvgMoveTo(NVGcontext* ctx, float x, float y); 410 | 411 | // Adds line segment from the last point in the path to the specified point. 412 | void nvgLineTo(NVGcontext* ctx, float x, float y); 413 | 414 | // Adds cubic bezier segment from last point in the path via two control points to the specified point. 415 | void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y); 416 | 417 | // Adds quadratic bezier segment from last point in the path via a control point to the specified point. 418 | void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y); 419 | 420 | // Adds an arc segment at the corner defined by the last path point, and two specified points. 421 | void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius); 422 | 423 | // Closes current sub-path with a line segment. 424 | void nvgClosePath(NVGcontext* ctx); 425 | 426 | // Sets the current sub-path winding, see NVGwinding and NVGsolidity. 427 | void nvgPathWinding(NVGcontext* ctx, int dir); 428 | 429 | // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, 430 | // and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW). 431 | // Angles are specified in radians. 432 | void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir); 433 | 434 | // Creates new rectangle shaped sub-path. 435 | void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); 436 | 437 | // Creates new rounded rectangle shaped sub-path. 438 | void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); 439 | 440 | // Creates new ellipse shaped sub-path. 441 | void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); 442 | 443 | // Creates new circle shaped sub-path. 444 | void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); 445 | 446 | // Fills the current path with current fill style. 447 | void nvgFill(NVGcontext* ctx); 448 | 449 | // Fills the current path with current stroke style. 450 | void nvgStroke(NVGcontext* ctx); 451 | 452 | 453 | // 454 | // Text 455 | // 456 | // NanoVG allows you to load .ttf files and use the font to render text. 457 | // 458 | // The appearance of the text can be defined by setting the current text style 459 | // and by specifying the fill color. Common text and font settings such as 460 | // font size, letter spacing and text align are supported. Font blur allows you 461 | // to create simple text effects such as drop shadows. 462 | // 463 | // At render time the font face can be set based on the font handles or name. 464 | // 465 | // Font measure functions return values in local space, the calculations are 466 | // carried in the same resolution as the final rendering. This is done because 467 | // the text glyph positions are snapped to the nearest pixels sharp rendering. 468 | // 469 | // The local space means that values are not rotated or scale as per the current 470 | // transformation. For example if you set font size to 12, which would mean that 471 | // line height is 16, then regardless of the current scaling and rotation, the 472 | // returned line height is always 16. Some measures may vary because of the scaling 473 | // since aforementioned pixel snapping. 474 | // 475 | // While this may sound a little odd, the setup allows you to always render the 476 | // same way regardless of scaling. I.e. following works regardless of scaling: 477 | // 478 | // const char* txt = "Text me up."; 479 | // nvgTextBounds(vg, x,y, txt, NULL, bounds); 480 | // nvgBeginPath(vg); 481 | // nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); 482 | // nvgFill(vg); 483 | // 484 | // Note: currently only solid color fill is supported for text. 485 | 486 | // Creates font by loading it from the disk from specified file name. 487 | // Returns handle to the font. 488 | int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); 489 | 490 | // Creates font by loading it from the specified memory chunk. 491 | // Returns handle to the font. 492 | int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); 493 | 494 | // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. 495 | int nvgFindFont(NVGcontext* ctx, const char* name); 496 | 497 | // Sets the font size of current text style. 498 | void nvgFontSize(NVGcontext* ctx, float size); 499 | 500 | // Sets the blur of current text style. 501 | void nvgFontBlur(NVGcontext* ctx, float blur); 502 | 503 | // Sets the letter spacing of current text style. 504 | void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); 505 | 506 | // Sets the proportional line height of current text style. The line height is specified as multiple of font size. 507 | void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); 508 | 509 | // Sets the text align of current text style, see NVGalign for options. 510 | void nvgTextAlign(NVGcontext* ctx, int align); 511 | 512 | // Sets the font face based on specified id of current text style. 513 | void nvgFontFaceId(NVGcontext* ctx, int font); 514 | 515 | // Sets the font face based on specified name of current text style. 516 | void nvgFontFace(NVGcontext* ctx, const char* font); 517 | 518 | // Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. 519 | float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end); 520 | 521 | // Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. 522 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 523 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 524 | void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end); 525 | 526 | // Measures the specified text string. Parameter bounds should be a pointer to float[4], 527 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 528 | // Returns the horizontal advance of the measured text (i.e. where the next character should drawn). 529 | // Measured values are returned in local coordinate space. 530 | float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds); 531 | 532 | // Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], 533 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 534 | // Measured values are returned in local coordinate space. 535 | void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds); 536 | 537 | // Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. 538 | // Measured values are returned in local coordinate space. 539 | int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions); 540 | 541 | // Returns the vertical metrics based on the current text style. 542 | // Measured values are returned in local coordinate space. 543 | void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh); 544 | 545 | // Breaks the specified text into lines. If end is specified only the sub-string will be used. 546 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 547 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 548 | int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows); 549 | 550 | // 551 | // Internal Render API 552 | // 553 | enum NVGtexture { 554 | NVG_TEXTURE_ALPHA = 0x01, 555 | NVG_TEXTURE_RGBA = 0x02, 556 | }; 557 | 558 | struct NVGscissor { 559 | float xform[6]; 560 | float extent[2]; 561 | }; 562 | typedef struct NVGscissor NVGscissor; 563 | 564 | struct NVGvertex { 565 | float x,y,u,v; 566 | }; 567 | typedef struct NVGvertex NVGvertex; 568 | 569 | struct NVGpath { 570 | int first; 571 | int count; 572 | unsigned char closed; 573 | int nbevel; 574 | NVGvertex* fill; 575 | int nfill; 576 | NVGvertex* stroke; 577 | int nstroke; 578 | int winding; 579 | int convex; 580 | }; 581 | typedef struct NVGpath NVGpath; 582 | 583 | struct NVGparams { 584 | void* userPtr; 585 | int edgeAntiAlias; 586 | int (*renderCreate)(void* uptr); 587 | int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); 588 | int (*renderDeleteTexture)(void* uptr, int image); 589 | int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); 590 | int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); 591 | void (*renderViewport)(void* uptr, int width, int height); 592 | void (*renderCancel)(void* uptr); 593 | void (*renderFlush)(void* uptr); 594 | void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); 595 | void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); 596 | void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts); 597 | void (*renderDelete)(void* uptr); 598 | }; 599 | typedef struct NVGparams NVGparams; 600 | 601 | // Constructor and destructor, called by the render back-end. 602 | NVGcontext* nvgCreateInternal(NVGparams* params); 603 | void nvgDeleteInternal(NVGcontext* ctx); 604 | 605 | NVGparams* nvgInternalParams(NVGcontext* ctx); 606 | 607 | // Debug function to dump cached path data. 608 | void nvgDebugDumpPathCache(NVGcontext* ctx); 609 | 610 | #ifdef _MSC_VER 611 | #pragma warning(pop) 612 | #endif 613 | 614 | #define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; } 615 | 616 | #ifdef __cplusplus 617 | } 618 | #endif 619 | 620 | #endif // NANOVG_H 621 | -------------------------------------------------------------------------------- /src/renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "vvg.hpp" 2 | #include "nanovg_vk.h" 3 | #include "nanovg.h" 4 | 5 | // vpp 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | // stl 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | // shader header 25 | #include "shader/fill.frag.h" 26 | #include "shader/fill.vert.h" 27 | 28 | namespace vvg { 29 | 30 | template constexpr void unused(T&&...) {} 31 | 32 | // minimal shader typedefs 33 | struct Vec2 { float x,y; }; 34 | struct Vec3 { float x,y,z; }; 35 | struct Vec4 { float x,y,z,w; }; 36 | 37 | using Mat2 = float[2][2]; 38 | using Mat3 = float[3][3]; 39 | using Mat4 = float[4][4]; 40 | 41 | struct UniformData { 42 | Vec2 viewSize; 43 | std::uint32_t type; 44 | std::uint32_t texType; 45 | Vec4 innerColor; 46 | Vec4 outerColor; 47 | Mat4 scissorMat; 48 | Mat4 paintMat; 49 | }; 50 | 51 | struct Path { 52 | std::size_t fillOffset = 0; 53 | std::size_t fillCount = 0; 54 | std::size_t strokeOffset = 0; 55 | std::size_t strokeCount = 0; 56 | }; 57 | 58 | struct DrawData { 59 | vpp::DescriptorSet descriptorSet; 60 | UniformData uniformData; 61 | unsigned int texture = 0; 62 | 63 | std::vector paths; 64 | std::size_t triangleOffset = 0; 65 | std::size_t triangleCount = 0; 66 | }; 67 | 68 | // The RenderBuilder implementation used to render on a swapchain. 69 | struct RenderImpl : public vpp::RendererBuilder { 70 | std::vector clearValues(unsigned int id) override; 71 | void build(unsigned int id, const vpp::RenderPassInstance& ini) override; 72 | void frame(unsigned int id) override; 73 | 74 | Renderer* renderer; 75 | vpp::SwapchainRenderer* swapchainRenderer; 76 | }; 77 | 78 | } // namespace vvg 79 | 80 | // vpp VulkanType specializations for our own shader types 81 | namespace vpp { 82 | 83 | template<> struct VulkanType : public VulkanTypeVec<2> {}; 84 | template<> struct VulkanType : public VulkanTypeVec<3> {}; 85 | template<> struct VulkanType : public VulkanTypeVec<4> {}; 86 | template<> struct VulkanType : public VulkanTypeMat<2, 2, true> {}; 87 | template<> struct VulkanType : public VulkanTypeMat<3, 3, true> {}; 88 | template<> struct VulkanType : public VulkanTypeMat<4, 4, true> {}; 89 | 90 | template<> struct VulkanType { 91 | static constexpr auto type = vpp::ShaderType::structure; 92 | static constexpr auto align = true; 93 | static constexpr auto members = std::make_tuple( 94 | &vvg::UniformData::viewSize, 95 | &vvg::UniformData::type, 96 | &vvg::UniformData::texType, 97 | &vvg::UniformData::innerColor, 98 | &vvg::UniformData::outerColor, 99 | &vvg::UniformData::scissorMat, 100 | &vvg::UniformData::paintMat 101 | ); 102 | }; 103 | 104 | } 105 | 106 | 107 | namespace vvg { 108 | 109 | //Renderer 110 | Renderer::Renderer(const vpp::Swapchain& swapchain, const vpp::Queue* presentQueue) 111 | : vpp::Resource(swapchain.device()), swapchain_(&swapchain), presentQueue_(presentQueue) 112 | { 113 | initRenderPass(swapchain.device(), swapchain.format()); 114 | init(); 115 | 116 | auto impl = std::make_unique(); 117 | impl->renderer = this; 118 | impl->swapchainRenderer = &renderer_; 119 | 120 | auto attachmentInfo = vpp::ViewableImage::defaultDepth2D(); 121 | attachmentInfo.imgInfo.format = vk::Format::s8Uint; 122 | attachmentInfo.viewInfo.format = vk::Format::s8Uint; 123 | attachmentInfo.viewInfo.subresourceRange.aspectMask = vk::ImageAspectBits::stencil; 124 | 125 | vpp::SwapchainRenderer::CreateInfo info {renderPass_, 0, {{attachmentInfo}}}; 126 | renderer_ = {swapchain, info, std::move(impl)}; 127 | } 128 | 129 | Renderer::Renderer(const vpp::Framebuffer& framebuffer, vk::RenderPass rp) 130 | : vpp::Resource(framebuffer.device()), framebuffer_(&framebuffer), renderPassHandle_(rp) 131 | { 132 | init(); 133 | commandBuffer_ = framebuffer.device().commandProvider().get(renderQueue_->family()); 134 | } 135 | 136 | 137 | Renderer::~Renderer() 138 | { 139 | 140 | } 141 | 142 | void Renderer::init() 143 | { 144 | // queues 145 | renderQueue_ = device().queue(vk::QueueBits::graphics); 146 | 147 | if(swapchain_ && !presentQueue_) { 148 | auto surface = swapchain_->vkSurface(); 149 | auto supported = vpp::supportedQueueFamilies(vkInstance(), surface, vkPhysicalDevice()); 150 | for(auto q : supported) 151 | if((presentQueue_ = device().queue(q))) 152 | break; 153 | 154 | if(!presentQueue_) 155 | throw std::runtime_error("vvg::Renderer::init: cannot find present queue"); 156 | } 157 | 158 | // sampler 159 | vk::SamplerCreateInfo samplerInfo; 160 | samplerInfo.magFilter = vk::Filter::linear; 161 | samplerInfo.minFilter = vk::Filter::linear; 162 | samplerInfo.mipmapMode = vk::SamplerMipmapMode::linear; 163 | samplerInfo.addressModeU = vk::SamplerAddressMode::clampToEdge; 164 | samplerInfo.addressModeV = vk::SamplerAddressMode::clampToEdge; 165 | samplerInfo.addressModeW = vk::SamplerAddressMode::clampToEdge; 166 | samplerInfo.mipLodBias = 0; 167 | samplerInfo.anisotropyEnable = true; 168 | samplerInfo.maxAnisotropy = 1; 169 | samplerInfo.compareEnable = false; 170 | samplerInfo.compareOp = {}; 171 | samplerInfo.minLod = 0; 172 | samplerInfo.maxLod = 0; 173 | samplerInfo.borderColor = vk::BorderColor::floatTransparentBlack; 174 | samplerInfo.unnormalizedCoordinates = false; 175 | sampler_ = {device(), samplerInfo}; 176 | 177 | // descLayout 178 | auto descriptorBindings = { 179 | vpp::descriptorBinding(vk::DescriptorType::uniformBuffer, 180 | vk::ShaderStageBits::vertex | vk::ShaderStageBits::fragment), 181 | vpp::descriptorBinding(vk::DescriptorType::combinedImageSampler, 182 | vk::ShaderStageBits::fragment, -1, 1, &sampler_.vkHandle()) 183 | }; 184 | 185 | descriptorLayout_ = {device(), descriptorBindings}; 186 | pipelineLayout_ = {device(), {descriptorLayout_}, {}}; 187 | 188 | //create the graphics pipeline 189 | // vpp::GraphicsPipelineBuilder builder(device(), vkRenderPass()); 190 | // builder.dynamicStates = {vk::DynamicState::viewport, vk::DynamicState::scissor}; 191 | // builder.states.rasterization.cullMode = vk::CullModeBits::none; 192 | // builder.states.inputAssembly.topology = vk::PrimitiveTopology::triangleFan; 193 | // builder.states.blendAttachments[0].blendEnable = true; 194 | // builder.states.blendAttachments[0].colorBlendOp = vk::BlendOp::add; 195 | // builder.states.blendAttachments[0].srcColorBlendFactor = vk::BlendFactor::srcAlpha; 196 | // builder.states.blendAttachments[0].dstColorBlendFactor = vk::BlendFactor::oneMinusSrcAlpha; 197 | // builder.states.blendAttachments[0].srcAlphaBlendFactor = vk::BlendFactor::one; 198 | // builder.states.blendAttachments[0].dstAlphaBlendFactor = vk::BlendFactor::zero; 199 | // builder.states.blendAttachments[0].alphaBlendOp = vk::BlendOp::add; 200 | // 201 | // vpp::VertexBufferLayout vertexLayout {{vk::Format::r32g32Sfloat, vk::Format::r32g32Sfloat}}; 202 | // builder.vertexBufferLayouts = {vertexLayout}; 203 | // builder.layout = pipelineLayout_; 204 | // 205 | // //shader 206 | // //the fragment shader has a constant for antialiasing 207 | // std::uint32_t antiAliasing = edgeAA_; 208 | // vk::SpecializationMapEntry entry {0, 0, 4}; 209 | // 210 | // vk::SpecializationInfo specInfo; 211 | // specInfo.mapEntryCount = 1; 212 | // specInfo.pMapEntries = &entry; 213 | // specInfo.dataSize = 4; 214 | // specInfo.pData = &antiAliasing; 215 | // 216 | // builder.shader.stage(fill_vert_data, {vk::ShaderStageBits::vertex}); 217 | // builder.shader.stage(fill_frag_data, {vk::ShaderStageBits::fragment, &specInfo}); 218 | // 219 | // fanPipeline_ = builder.build(); 220 | // 221 | // builder.states.inputAssembly.topology = vk::PrimitiveTopology::triangleStrip; 222 | // stripPipeline_ = builder.build(); 223 | // 224 | // builder.states.inputAssembly.topology = vk::PrimitiveTopology::triangleList; 225 | // listPipeline_ = builder.build(); 226 | 227 | 228 | std::uint32_t antiAliasing = edgeAA_; 229 | vk::SpecializationMapEntry entry {0, 0, 4}; 230 | 231 | vk::SpecializationInfo specInfo; 232 | specInfo.mapEntryCount = 1; 233 | specInfo.pMapEntries = &entry; 234 | specInfo.dataSize = 4; 235 | specInfo.pData = &antiAliasing; 236 | 237 | vpp::ShaderModule vertexShader(device(), fill_vert_data); 238 | vpp::ShaderModule fragmentShader(device(), fill_frag_data); 239 | 240 | vpp::ShaderProgram shaderStages({ 241 | {vertexShader, vk::ShaderStageBits::vertex}, 242 | {fragmentShader, vk::ShaderStageBits::fragment, &specInfo} 243 | }); 244 | 245 | vk::GraphicsPipelineCreateInfo pipelineInfo; 246 | pipelineInfo.renderPass = vkRenderPass(); 247 | pipelineInfo.layout = pipelineLayout_; 248 | 249 | pipelineInfo.stageCount = shaderStages.vkStageInfos().size(); 250 | pipelineInfo.pStages = shaderStages.vkStageInfos().data(); 251 | 252 | constexpr auto stride = (2 * 4) * 2; // 2 pos floats, 2 uv floats 253 | vk::VertexInputBindingDescription bufferBinding {0, stride, vk::VertexInputRate::vertex}; 254 | 255 | // vertex position, uv attributes 256 | vk::VertexInputAttributeDescription attributes[2]; 257 | attributes[0].format = vk::Format::r32g32Sfloat; 258 | 259 | attributes[1].location = 1; 260 | attributes[1].format = vk::Format::r32g32Sfloat; 261 | attributes[1].offset = 2 * 4; // offset pos (vec2f) 262 | 263 | vk::PipelineVertexInputStateCreateInfo vertexInfo; 264 | vertexInfo.vertexBindingDescriptionCount = 1; 265 | vertexInfo.pVertexBindingDescriptions = &bufferBinding; 266 | vertexInfo.vertexAttributeDescriptionCount = 2; 267 | vertexInfo.pVertexAttributeDescriptions = attributes; 268 | pipelineInfo.pVertexInputState = &vertexInfo; 269 | 270 | vk::PipelineInputAssemblyStateCreateInfo assemblyInfo; 271 | assemblyInfo.topology = vk::PrimitiveTopology::triangleList; 272 | pipelineInfo.pInputAssemblyState = &assemblyInfo; 273 | 274 | vk::PipelineRasterizationStateCreateInfo rasterizationInfo; 275 | rasterizationInfo.polygonMode = vk::PolygonMode::fill; 276 | rasterizationInfo.cullMode = vk::CullModeBits::none; 277 | rasterizationInfo.frontFace = vk::FrontFace::counterClockwise; 278 | rasterizationInfo.depthClampEnable = false; 279 | rasterizationInfo.rasterizerDiscardEnable = false; 280 | rasterizationInfo.depthBiasEnable = false; 281 | rasterizationInfo.lineWidth = 1.f; 282 | pipelineInfo.pRasterizationState = &rasterizationInfo; 283 | 284 | vk::PipelineMultisampleStateCreateInfo multisampleInfo; 285 | multisampleInfo.rasterizationSamples = vk::SampleCountBits::e1; 286 | pipelineInfo.pMultisampleState = &multisampleInfo; 287 | 288 | vk::PipelineColorBlendAttachmentState blendAttachment; 289 | blendAttachment.blendEnable = true; 290 | blendAttachment.alphaBlendOp = vk::BlendOp::add; 291 | blendAttachment.colorBlendOp = vk::BlendOp::add; 292 | blendAttachment.srcColorBlendFactor = vk::BlendFactor::srcAlpha; 293 | blendAttachment.dstColorBlendFactor = vk::BlendFactor::oneMinusSrcAlpha; 294 | blendAttachment.srcAlphaBlendFactor = vk::BlendFactor::one; 295 | blendAttachment.dstAlphaBlendFactor = vk::BlendFactor::zero; 296 | blendAttachment.colorWriteMask = 297 | vk::ColorComponentBits::r | 298 | vk::ColorComponentBits::g | 299 | vk::ColorComponentBits::b | 300 | vk::ColorComponentBits::a; 301 | 302 | vk::PipelineColorBlendStateCreateInfo blendInfo; 303 | blendInfo.attachmentCount = 1; 304 | blendInfo.pAttachments = &blendAttachment; 305 | pipelineInfo.pColorBlendState = &blendInfo; 306 | 307 | vk::PipelineViewportStateCreateInfo viewportInfo; 308 | viewportInfo.scissorCount = 1; 309 | viewportInfo.viewportCount = 1; 310 | pipelineInfo.pViewportState = &viewportInfo; 311 | 312 | vk::PipelineDepthStencilStateCreateInfo depthStencilInfo; 313 | pipelineInfo.pDepthStencilState = &depthStencilInfo; 314 | 315 | constexpr auto dynStates = {vk::DynamicState::viewport, vk::DynamicState::scissor}; 316 | 317 | vk::PipelineDynamicStateCreateInfo dynamicInfo; 318 | dynamicInfo.dynamicStateCount = dynStates.size(); 319 | dynamicInfo.pDynamicStates = dynStates.begin(); 320 | pipelineInfo.pDynamicState = &dynamicInfo; 321 | 322 | // copy for strip pipeline 323 | auto stripInfo = pipelineInfo; 324 | auto stripAssembly = assemblyInfo; 325 | stripAssembly.topology = vk::PrimitiveTopology::triangleStrip; 326 | stripInfo.pInputAssemblyState = &stripAssembly; 327 | stripInfo.basePipelineIndex = 0; 328 | 329 | // copy for fan pipelien 330 | auto fanInfo = pipelineInfo; 331 | auto fanAssembly = assemblyInfo; 332 | fanAssembly.topology = vk::PrimitiveTopology::triangleFan; 333 | fanInfo.pInputAssemblyState = &fanAssembly; 334 | fanInfo.basePipelineIndex = 0; 335 | 336 | constexpr auto cacheName = "grapihcsPipelineCache.bin"; 337 | 338 | vpp::PipelineCache cache; 339 | if(vpp::fileExists(cacheName)) cache = {device(), cacheName}; 340 | else cache = {device()}; 341 | auto pipelines = vk::createGraphicsPipelines(device(), cache, 342 | {pipelineInfo, stripInfo, fanInfo}); 343 | 344 | listPipeline_ = {device(), pipelines[0]}; 345 | stripPipeline_ = {device(), pipelines[1]}; 346 | fanPipeline_ = {device(), pipelines[2]}; 347 | 348 | // save the cache to the file we tried to load it from 349 | vpp::save(cache, cacheName); 350 | 351 | // create a dummy image used for unbound image descriptors 352 | // TODO: find out if this is actually needed or a bug in the layers 353 | dummyTexture_ = {device(), (unsigned int) -1, {2, 2}, vk::Format::r8g8b8a8Unorm}; 354 | } 355 | 356 | unsigned int Renderer::createTexture(vk::Format format, unsigned int w, unsigned int h, 357 | const std::uint8_t* data) 358 | { 359 | ++texID_; 360 | textures_.emplace_back(device(), texID_, vk::Extent2D{w, h}, format, data); 361 | return texID_; 362 | } 363 | 364 | bool Renderer::deleteTexture(unsigned int id) 365 | { 366 | auto it = std::find_if(textures_.begin(), textures_.end(), 367 | [=](const auto& tex) { return tex.id() == id; }); 368 | 369 | if(it == textures_.end()) return false; 370 | 371 | textures_.erase(it); 372 | return true; 373 | } 374 | 375 | void Renderer::start(unsigned int width, unsigned int height) 376 | { 377 | // store (and set) viewport in some way 378 | width_ = width; 379 | height_ = height; 380 | 381 | vertices_.clear(); 382 | drawDatas_.clear(); 383 | } 384 | 385 | void Renderer::cancel() 386 | { 387 | } 388 | 389 | void Renderer::flush() 390 | { 391 | if(drawDatas_.empty()) 392 | return; 393 | 394 | // allocate buffers 395 | auto uniformSize = sizeof(UniformData) * drawDatas_.size(); 396 | auto bits = device().memoryTypeBits(vk::MemoryPropertyBits::hostVisible); 397 | 398 | if(uniformBuffer_.memorySize() < uniformSize) { 399 | vk::BufferCreateInfo bufInfo; 400 | bufInfo.usage = vk::BufferUsageBits::uniformBuffer; 401 | bufInfo.size = uniformSize; 402 | uniformBuffer_ = {device(), bufInfo, bits}; 403 | } 404 | 405 | auto vertexSize = vertices_.size() * sizeof(NVGvertex); 406 | if(vertexBuffer_.memorySize() < vertexSize) { 407 | vk::BufferCreateInfo bufInfo; 408 | bufInfo.usage = vk::BufferUsageBits::vertexBuffer; 409 | bufInfo.size = vertexSize; 410 | vertexBuffer_ = {device(), bufInfo, bits}; 411 | } 412 | 413 | // descriptorPool 414 | if(drawDatas_.size() > descriptorPoolSize_) { 415 | vk::DescriptorPoolSize typeCounts[2]; 416 | typeCounts[0].type = vk::DescriptorType::uniformBuffer; 417 | typeCounts[0].descriptorCount = drawDatas_.size(); 418 | 419 | typeCounts[1].type = vk::DescriptorType::combinedImageSampler; 420 | typeCounts[1].descriptorCount = drawDatas_.size(); 421 | 422 | vk::DescriptorPoolCreateInfo poolInfo; 423 | poolInfo.poolSizeCount = 2; 424 | poolInfo.pPoolSizes = typeCounts; 425 | poolInfo.maxSets = drawDatas_.size(); 426 | 427 | descriptorPool_ = {device(), poolInfo}; 428 | descriptorPoolSize_ = drawDatas_.size(); 429 | } else if(descriptorPool_) { 430 | vk::resetDescriptorPool(device(), descriptorPool_, {}); 431 | } 432 | 433 | // update 434 | vpp::BufferUpdate update(uniformBuffer_, vpp::BufferLayout::std140); 435 | for(auto& data : drawDatas_) { 436 | update.alignUniform(); 437 | 438 | auto offset = update.offset(); 439 | update.add(vpp::raw(data.uniformData, 1)); // TODO 440 | 441 | data.descriptorSet = {descriptorLayout_, descriptorPool_}; 442 | 443 | vpp::DescriptorSetUpdate descUpdate(data.descriptorSet); 444 | descUpdate.uniform({{uniformBuffer_, offset, sizeof(UniformData)}}); 445 | 446 | vk::ImageView iv = dummyTexture_.viewableImage().vkImageView(); 447 | dlg_assert(iv); 448 | if(data.texture != 0) 449 | iv = texture(data.texture)->viewableImage().vkImageView(); 450 | 451 | auto layout = vk::ImageLayout::general; //XXX 452 | descUpdate.imageSampler({{{}, iv, layout}}); 453 | 454 | descUpdate.apply(); 455 | } 456 | 457 | update.apply()->finish(); 458 | 459 | //vertex 460 | vpp::BufferUpdate vupdate(vertexBuffer_, vpp::BufferLayout::std140); 461 | vupdate.add(vpp::raw(*vertices_.data(), vertices_.size())); 462 | vupdate.apply()->finish(); 463 | 464 | //render 465 | if(swapchain_) { 466 | renderer_.renderBlock(*presentQueue_); 467 | } else { 468 | vk::beginCommandBuffer(commandBuffer_, {}); 469 | 470 | vk::ClearValue clearValues[2] {}; 471 | clearValues[0].color = {0.f, 0.f, 0.f, 1.0f}; 472 | clearValues[1].depthStencil = {1.f, 0}; 473 | 474 | auto size = framebuffer_->size(); 475 | 476 | vk::RenderPassBeginInfo beginInfo; 477 | beginInfo.renderPass = vkRenderPass(); 478 | beginInfo.renderArea = {{0, 0}, {size.width, size.height}}; 479 | beginInfo.clearValueCount = 2; 480 | beginInfo.pClearValues = clearValues; 481 | beginInfo.framebuffer = *framebuffer_; 482 | vk::cmdBeginRenderPass(commandBuffer_, beginInfo, vk::SubpassContents::eInline); 483 | 484 | vk::Viewport viewport; 485 | viewport.width = size.width; 486 | viewport.height = size.height; 487 | viewport.minDepth = 0.f; 488 | viewport.maxDepth = 1.f; 489 | vk::cmdSetViewport(commandBuffer_, 0, 1, viewport); 490 | 491 | //Update dynamic scissor state 492 | vk::Rect2D scissor; 493 | scissor.extent = {size.width, size.height}; 494 | scissor.offset = {0, 0}; 495 | vk::cmdSetScissor(commandBuffer_, 0, 1, scissor); 496 | 497 | record(commandBuffer_); 498 | 499 | vk::cmdEndRenderPass(commandBuffer_); 500 | vk::endCommandBuffer(commandBuffer_); 501 | 502 | vpp::CommandExecutionState state; 503 | device().submitManager().add(*renderQueue_, {commandBuffer_}, &state); 504 | state.wait(); 505 | } 506 | 507 | //cleanup 508 | vertices_.clear(); 509 | drawDatas_.clear(); 510 | } 511 | 512 | void Renderer::fill(const NVGpaint& paint, const NVGscissor& scissor, float fringe, 513 | const float* bounds, nytl::Span paths) 514 | { 515 | unused(bounds); 516 | auto& drawData = parsePaint(paint, scissor, fringe, fringe); 517 | drawData.paths.reserve(paths.size()); 518 | 519 | for(auto& path : paths) 520 | { 521 | drawData.paths.emplace_back(); 522 | drawData.paths.back().fillOffset = vertices_.size(); 523 | drawData.paths.back().fillCount = path.nfill; 524 | vertices_.insert(vertices_.end(), path.fill, path.fill + path.nfill); 525 | 526 | if(edgeAA_ && path.nstroke > 0) 527 | { 528 | drawData.paths.back().strokeOffset = vertices_.size(); 529 | drawData.paths.back().strokeCount = path.nstroke; 530 | vertices_.insert(vertices_.end(), path.stroke, path.stroke + path.nstroke); 531 | } 532 | } 533 | } 534 | void Renderer::stroke(const NVGpaint& paint, const NVGscissor& scissor, float fringe, 535 | float strokeWidth, nytl::Span paths) 536 | { 537 | auto& drawData = parsePaint(paint, scissor, fringe, strokeWidth); 538 | drawData.paths.reserve(paths.size()); 539 | 540 | for(auto& path : paths) 541 | { 542 | drawData.paths.emplace_back(); 543 | drawData.paths.back().strokeOffset = vertices_.size(); 544 | drawData.paths.back().strokeCount = path.nstroke; 545 | vertices_.insert(vertices_.end(), path.stroke, path.stroke + path.nstroke); 546 | } 547 | } 548 | void Renderer::triangles(const NVGpaint& paint, const NVGscissor& scissor, 549 | nytl::Span verts) 550 | { 551 | auto& drawData = parsePaint(paint, scissor, 1.f, 1.f); 552 | 553 | drawData.triangleOffset = vertices_.size(); 554 | drawData.triangleCount = verts.size(); 555 | vertices_.insert(vertices_.end(), verts.begin(), verts.end()); 556 | } 557 | 558 | DrawData& Renderer::parsePaint(const NVGpaint& paint, const NVGscissor& scissor, float fringe, 559 | float strokeWidth) 560 | { 561 | static constexpr auto typeColor = 1; 562 | static constexpr auto typeGradient = 2; 563 | static constexpr auto typeTexture = 3; 564 | 565 | static constexpr auto texTypeRGBA = 1; 566 | static constexpr auto texTypeA = 2; 567 | 568 | //update image 569 | drawDatas_.emplace_back(); 570 | 571 | auto& data = drawDatas_.back(); 572 | data.uniformData.viewSize = {float(width_), float(height_)}; 573 | 574 | if(paint.image) { 575 | auto* tex = texture(paint.image); 576 | 577 | auto formatID = (tex->format() == vk::Format::r8g8b8a8Unorm) ? texTypeRGBA : texTypeA; 578 | data.uniformData.type = typeTexture; 579 | data.uniformData.texType = formatID; 580 | 581 | data.texture = paint.image; 582 | } else if(std::memcmp(&paint.innerColor, &paint.outerColor, sizeof(paint.innerColor)) == 0) { 583 | data.uniformData.type = typeColor; 584 | data.uniformData.texType = 0u; 585 | } else { 586 | data.uniformData.type = typeGradient; 587 | data.uniformData.texType = 0u; 588 | } 589 | 590 | //colors 591 | std::memcpy(&data.uniformData.innerColor, &paint.innerColor, sizeof(float) * 4); 592 | std::memcpy(&data.uniformData.outerColor, &paint.outerColor, sizeof(float) * 4); 593 | 594 | //mats 595 | float invxform[6]; 596 | 597 | //scissor 598 | float scissorMat[4][4] {}; 599 | if (scissor.extent[0] < -0.5f || scissor.extent[1] < -0.5f) { 600 | scissorMat[3][0] = 1.0f; 601 | scissorMat[3][1] = 1.0f; 602 | scissorMat[3][2] = 1.0f; 603 | scissorMat[3][3] = 1.0f; 604 | } else { 605 | nvgTransformInverse(invxform, scissor.xform); 606 | 607 | scissorMat[0][0] = invxform[0]; 608 | scissorMat[0][1] = invxform[1]; 609 | scissorMat[1][0] = invxform[2]; 610 | scissorMat[1][1] = invxform[3]; 611 | scissorMat[2][0] = invxform[4]; 612 | scissorMat[2][1] = invxform[5]; 613 | scissorMat[2][2] = 1.0f; 614 | 615 | //extent 616 | scissorMat[3][0] = scissor.extent[0]; 617 | scissorMat[3][1] = scissor.extent[1]; 618 | 619 | //scale 620 | scissorMat[3][2] = std::sqrt(scissor.xform[0]*scissor.xform[0] + scissor.xform[2]* 621 | scissor.xform[2]) / fringe; 622 | scissorMat[3][3] = std::sqrt(scissor.xform[1]*scissor.xform[1] + scissor.xform[3]* 623 | scissor.xform[3]) / fringe; 624 | } 625 | 626 | scissorMat[0][3] = paint.radius; 627 | scissorMat[1][3] = paint.feather; 628 | scissorMat[2][3] = strokeWidth; 629 | 630 | std::memcpy(&data.uniformData.scissorMat, &scissorMat, sizeof(scissorMat)); 631 | 632 | //paint 633 | float paintMat[4][4] {}; 634 | nvgTransformInverse(invxform, paint.xform); 635 | paintMat[0][0] = invxform[0]; 636 | paintMat[0][1] = invxform[1]; 637 | paintMat[1][0] = invxform[2]; 638 | paintMat[1][1] = invxform[3]; 639 | paintMat[2][0] = invxform[4]; 640 | paintMat[2][1] = invxform[5]; 641 | paintMat[2][2] = 1.0f; 642 | 643 | paintMat[3][0] = paint.extent[0]; 644 | paintMat[3][1] = paint.extent[1]; 645 | 646 | //strokeMult 647 | paintMat[0][3] = (strokeWidth * 0.5f + fringe * 0.5f) / fringe; 648 | 649 | std::memcpy(&data.uniformData.paintMat, &paintMat, sizeof(paintMat)); 650 | return data; 651 | } 652 | 653 | const Texture* Renderer::texture(unsigned int id) const 654 | { 655 | auto it = std::find_if(textures_.begin(), textures_.end(), 656 | [=](const auto& tex) { return tex.id() == id; }); 657 | 658 | if(it == textures_.end()) return nullptr; 659 | return &(*it); 660 | } 661 | 662 | Texture* Renderer::texture(unsigned int id) 663 | { 664 | auto it = std::find_if(textures_.begin(), textures_.end(), 665 | [=](const auto& tex) { return tex.id() == id; }); 666 | 667 | if(it == textures_.end()) return nullptr; 668 | return &(*it); 669 | } 670 | 671 | void Renderer::record(vk::CommandBuffer cmdBuffer) 672 | { 673 | int bound = 0; 674 | vk::cmdBindVertexBuffers(cmdBuffer, 0, {vertexBuffer_}, {0}); 675 | 676 | for(auto& data : drawDatas_) 677 | { 678 | vk::cmdBindDescriptorSets(cmdBuffer, vk::PipelineBindPoint::graphics, pipelineLayout_, 679 | 0, {data.descriptorSet}, {}); 680 | 681 | for(auto& path : data.paths) { 682 | if(path.fillCount > 0) { 683 | if(bound != 1) { 684 | vk::cmdBindPipeline(cmdBuffer, vk::PipelineBindPoint::graphics, fanPipeline_); 685 | bound = 1; 686 | } 687 | 688 | vk::cmdDraw(cmdBuffer, path.fillCount, 1, path.fillOffset, 0); 689 | } if(path.strokeCount > 0) { 690 | if(bound != 2) { 691 | vk::cmdBindPipeline(cmdBuffer, vk::PipelineBindPoint::graphics, stripPipeline_); 692 | bound = 2; 693 | } 694 | 695 | vk::cmdDraw(cmdBuffer, path.strokeCount, 1, path.strokeOffset, 0); 696 | } 697 | } 698 | 699 | if(data.triangleCount > 0) { 700 | if(bound != 3) { 701 | vk::cmdBindPipeline(cmdBuffer, vk::PipelineBindPoint::graphics, listPipeline_); 702 | bound = 3; 703 | } 704 | 705 | vk::cmdDraw(cmdBuffer, data.triangleCount, 1, data.triangleOffset, 0); 706 | } 707 | } 708 | } 709 | 710 | void Renderer::initRenderPass(const vpp::Device& dev, vk::Format attachment) 711 | { 712 | vk::AttachmentDescription attachments[2] {}; 713 | 714 | //color from swapchain 715 | attachments[0].format = attachment; 716 | attachments[0].samples = vk::SampleCountBits::e1; 717 | attachments[0].loadOp = vk::AttachmentLoadOp::clear; 718 | attachments[0].storeOp = vk::AttachmentStoreOp::store; 719 | attachments[0].stencilLoadOp = vk::AttachmentLoadOp::dontCare; 720 | attachments[0].stencilStoreOp = vk::AttachmentStoreOp::dontCare; 721 | attachments[0].initialLayout = vk::ImageLayout::undefined; 722 | attachments[0].finalLayout = vk::ImageLayout::presentSrcKHR; 723 | 724 | vk::AttachmentReference colorReference; 725 | colorReference.attachment = 0; 726 | colorReference.layout = vk::ImageLayout::colorAttachmentOptimal; 727 | 728 | //stencil attachment 729 | //will not be used as depth buffer 730 | attachments[1].format = vk::Format::s8Uint; 731 | attachments[1].samples = vk::SampleCountBits::e1; 732 | attachments[1].loadOp = vk::AttachmentLoadOp::clear; 733 | attachments[1].storeOp = vk::AttachmentStoreOp::store; 734 | attachments[1].stencilLoadOp = vk::AttachmentLoadOp::dontCare; 735 | attachments[1].stencilStoreOp = vk::AttachmentStoreOp::dontCare; 736 | attachments[1].initialLayout = vk::ImageLayout::undefined; 737 | attachments[1].finalLayout = vk::ImageLayout::depthStencilAttachmentOptimal; 738 | // attachments[1].initialLayout = vk::ImageLayout::depthStencilAttachmentOptimal; 739 | // attachments[1].finalLayout = vk::ImageLayout::depthStencilAttachmentOptimal; 740 | 741 | vk::AttachmentReference depthReference; 742 | depthReference.attachment = 1; 743 | depthReference.layout = vk::ImageLayout::depthStencilAttachmentOptimal; 744 | 745 | //only subpass 746 | vk::SubpassDescription subpass; 747 | subpass.pipelineBindPoint = vk::PipelineBindPoint::graphics; 748 | subpass.flags = {}; 749 | subpass.inputAttachmentCount = 0; 750 | subpass.pInputAttachments = nullptr; 751 | subpass.colorAttachmentCount = 1; 752 | subpass.pColorAttachments = &colorReference; 753 | subpass.pResolveAttachments = nullptr; 754 | subpass.pDepthStencilAttachment = &depthReference; 755 | subpass.preserveAttachmentCount = 0; 756 | subpass.pPreserveAttachments = nullptr; 757 | 758 | vk::RenderPassCreateInfo renderPassInfo; 759 | renderPassInfo.attachmentCount = 2; 760 | renderPassInfo.pAttachments = attachments; 761 | renderPassInfo.subpassCount = 1; 762 | renderPassInfo.pSubpasses = &subpass; 763 | renderPassInfo.dependencyCount = 0; 764 | renderPassInfo.pDependencies = nullptr; 765 | 766 | renderPass_ = {dev, renderPassInfo}; 767 | } 768 | 769 | 770 | //Texture 771 | Texture::Texture(const vpp::Device& dev, unsigned int xid, const vk::Extent2D& size, 772 | vk::Format format, const std::uint8_t* data) 773 | : format_(format), id_(xid), width_(size.width), height_(size.height) 774 | { 775 | vk::Extent3D extent {width(), height(), 1}; 776 | 777 | auto info = vpp::ViewableImage::defaultColor2D(); 778 | info.imgInfo.extent = extent; 779 | info.imgInfo.initialLayout = vk::ImageLayout::undefined; 780 | info.imgInfo.tiling = vk::ImageTiling::linear; 781 | 782 | info.imgInfo.format = format; 783 | info.viewInfo.format = format; 784 | 785 | info.imgInfo.usage = vk::ImageUsageBits::sampled; 786 | info.memoryTypeBits = dev.memoryTypeBits(vk::MemoryPropertyBits::hostVisible); 787 | 788 | // info.imgInfo.usage = vk::ImageUsageBits::transferDst | vk::ImageUsageBits::sampled; 789 | viewableImage_ = {dev, info}; 790 | 791 | vpp::changeLayout(viewableImage_.image(), vk::ImageLayout::undefined, 792 | vk::ImageLayout::general, {vk::ImageAspectBits::color, 0, 1, 0, 1})->finish(); 793 | 794 | vk::ImageLayout layout = vk::ImageLayout::general; 795 | if(data) 796 | vpp::fill(viewableImage_.image(), *data, format, layout, extent, 797 | {vk::ImageAspectBits::color, 0, 1})->finish(); 798 | } 799 | 800 | void Texture::update(const vk::Offset2D& offset, const vk::Extent2D& extent, 801 | const std::uint8_t& data) 802 | { 803 | unused(offset, extent); 804 | 805 | //TODO: really only update the given offset/extent 806 | //would need an extra data copy 807 | //or modify vpp::fill(Image) to also accept non tightly packed data 808 | //or just fill it manually... 809 | // vk::Offset3D ioffset {offset.x, offset.y, 0}; 810 | 811 | vk::Extent3D iextent {width(), height(), 1}; 812 | vk::ImageLayout layout = vk::ImageLayout::general; 813 | fill(viewableImage_.image(), data, format(), layout, iextent, 814 | {vk::ImageAspectBits::color, 0, 0})->finish(); 815 | } 816 | 817 | 818 | //RenderImpl 819 | void RenderImpl::build(unsigned int, const vpp::RenderPassInstance& ini) 820 | { 821 | renderer->record(ini.vkCommandBuffer()); 822 | } 823 | 824 | std::vector RenderImpl::clearValues(unsigned int) 825 | { 826 | // TODO 827 | std::vector ret(1, vk::ClearValue{}); 828 | ret[0].color = {0.f, 0.f, 0.f, 1.0f}; 829 | // ret[0].depthStencil = {1.f, 0}; 830 | return ret; 831 | } 832 | 833 | void RenderImpl::frame(unsigned int id) 834 | { 835 | swapchainRenderer->record(id); 836 | } 837 | 838 | //class that derives vvg::Renderer for the C implementation. 839 | using NonOwnedDevicePtr = std::unique_ptr>; 840 | using NonOwnedSwapchainPtr = std::unique_ptr>; 841 | 842 | class RendererCImpl : public Renderer { 843 | public: 844 | RendererCImpl(NonOwnedDevicePtr dev, NonOwnedSwapchainPtr swapchain) 845 | : Renderer(*swapchain), dev_(std::move(dev)), swapchain_(std::move(swapchain)) {} 846 | 847 | virtual ~RendererCImpl() 848 | { 849 | //first destruct the Renderer since it may depend on the device and swapchain 850 | Renderer::operator=({}); 851 | } 852 | 853 | protected: 854 | NonOwnedDevicePtr dev_; 855 | NonOwnedSwapchainPtr swapchain_; 856 | }; 857 | 858 | } // namespace vvg 859 | 860 | // The NVGcontext backend implementation which just calls the Renderer/Texture member functions 861 | namespace { 862 | 863 | vvg::Renderer& resolve(void* ptr) 864 | { 865 | return *static_cast(ptr); 866 | } 867 | 868 | int renderCreate(void* uptr) 869 | { 870 | vvg::unused(uptr); 871 | return 1; 872 | } 873 | 874 | int createTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data) 875 | { 876 | vvg::unused(uptr, type, imageFlags); 877 | auto& renderer = resolve(uptr); 878 | auto format = (type == NVG_TEXTURE_ALPHA) ? vk::Format::r8Unorm : vk::Format::r8g8b8a8Unorm; 879 | return renderer.createTexture(format, w, h, data); 880 | } 881 | int deleteTexture(void* uptr, int image) 882 | { 883 | auto& renderer = resolve(uptr); 884 | return renderer.deleteTexture(image); 885 | } 886 | int updateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) 887 | { 888 | auto& renderer = resolve(uptr); 889 | auto* tex = renderer.texture(image); 890 | if(!tex) return 0; 891 | 892 | vk::Extent2D extent {(unsigned int) w, (unsigned int) h}; 893 | vk::Offset2D offset{x, y}; 894 | tex->update(offset, extent, *data); 895 | 896 | return 1; 897 | } 898 | int getTextureSize(void* uptr, int image, int* w, int* h) 899 | { 900 | auto& renderer = resolve(uptr); 901 | auto* tex = renderer.texture(image); 902 | if(!tex) return 0; 903 | 904 | //TODO: first check pointers? 905 | *w = tex->width(); 906 | *h = tex->width(); 907 | return 1; 908 | } 909 | void viewport(void* uptr, int width, int height) 910 | { 911 | auto& renderer = resolve(uptr); 912 | renderer.start(width, height); 913 | } 914 | void cancel(void* uptr) 915 | { 916 | auto& renderer = resolve(uptr); 917 | renderer.cancel(); 918 | } 919 | void flush(void* uptr) 920 | { 921 | auto& renderer = resolve(uptr); 922 | renderer.flush(); 923 | } 924 | void fill(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, 925 | const NVGpath* paths, int npaths) 926 | { 927 | auto& renderer = resolve(uptr); 928 | renderer.fill(*paint, *scissor, fringe, bounds, {paths, std::size_t(npaths)}); 929 | } 930 | void stroke(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, 931 | const NVGpath* paths, int npaths) 932 | { 933 | auto& renderer = resolve(uptr); 934 | renderer.stroke(*paint, *scissor, fringe, strokeWidth, {paths, std::size_t(npaths)}); 935 | } 936 | void triangles(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts) 937 | { 938 | auto& renderer = resolve(uptr); 939 | renderer.triangles(*paint, *scissor, {verts, std::size_t(nverts)}); 940 | } 941 | void renderDelete(void* uptr) 942 | { 943 | auto& renderer = resolve(uptr); 944 | delete &renderer; 945 | } 946 | 947 | const NVGparams nvgContextImpl = 948 | { 949 | nullptr, 950 | 1, 951 | renderCreate, 952 | createTexture, 953 | deleteTexture, 954 | updateTexture, 955 | getTextureSize, 956 | viewport, 957 | cancel, 958 | flush, 959 | fill, 960 | stroke, 961 | triangles, 962 | renderDelete 963 | }; 964 | 965 | } // anonymous util namespace 966 | 967 | // implementation of the C++ create api 968 | namespace vvg { 969 | 970 | NVGcontext* createContext(std::unique_ptr renderer) 971 | { 972 | auto impl = nvgContextImpl; 973 | auto rendererPtr = renderer.get(); 974 | impl.userPtr = renderer.release(); 975 | auto ret = nvgCreateInternal(&impl); 976 | if(!ret) delete rendererPtr; 977 | return ret; 978 | } 979 | 980 | NVGcontext* createContext(const vpp::Swapchain& swapchain) 981 | { 982 | return createContext(std::make_unique(swapchain)); 983 | } 984 | 985 | NVGcontext* createContext(const vpp::Framebuffer& fb, vk::RenderPass rp) 986 | { 987 | return createContext(std::make_unique(fb, rp)); 988 | } 989 | 990 | void destroyContext(const NVGcontext& context) 991 | { 992 | auto ctx = const_cast(&context); 993 | nvgDeleteInternal(ctx); 994 | } 995 | 996 | const Renderer& getRenderer(const NVGcontext& context) 997 | { 998 | auto ctx = const_cast(&context); 999 | return resolve(nvgInternalParams(ctx)->userPtr); 1000 | } 1001 | Renderer& getRenderer(NVGcontext& context) 1002 | { 1003 | return resolve(nvgInternalParams(&context)->userPtr); 1004 | } 1005 | 1006 | } 1007 | 1008 | // implementation of the C api 1009 | NVGcontext* vvgCreate(const VVGContextDescription* descr) 1010 | { 1011 | auto vkInstance = (vk::Instance)descr->instance; 1012 | auto vkPhDev = (vk::PhysicalDevice)descr->phDev; 1013 | auto vkDev = (vk::Device)descr->device; 1014 | auto vkQueue = (vk::Queue)descr->queue; 1015 | auto vkSwapchain = (vk::SwapchainKHR)descr->swapchain; 1016 | auto vkFormat = (vk::Format)descr->swapchainFormat; 1017 | auto vkExtent = (const vk::Extent2D&)descr->swapchainSize; 1018 | 1019 | vvg::NonOwnedDevicePtr dev(new vpp::NonOwned(vkInstance, 1020 | vkPhDev, vkDev, {{vkQueue, descr->queueFamily}})); 1021 | 1022 | // NOTE that this constructs the swapchain with an invalid surface parameter and it 1023 | // can therefore not be reiszed. 1024 | vvg::NonOwnedSwapchainPtr swapchain(new vpp::NonOwned(*dev, 1025 | vkSwapchain, {}, vkExtent, vkFormat)); 1026 | 1027 | auto renderer = std::make_unique(std::move(dev), std::move(swapchain)); 1028 | return vvg::createContext(std::move(renderer)); 1029 | } 1030 | 1031 | void vvgDestroy(const NVGcontext* context) 1032 | { 1033 | auto ctx = const_cast(context); 1034 | nvgDeleteInternal(ctx); 1035 | } 1036 | -------------------------------------------------------------------------------- /examples/stb_image_write.h: -------------------------------------------------------------------------------- 1 | /* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h 2 | writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 3 | no warranty implied; use at your own risk 4 | 5 | Before #including, 6 | 7 | #define STB_IMAGE_WRITE_IMPLEMENTATION 8 | 9 | in the file that you want to have the implementation. 10 | 11 | Will probably not work correctly with strict-aliasing optimizations. 12 | 13 | ABOUT: 14 | 15 | This header file is a library for writing images to C stdio. It could be 16 | adapted to write to memory or a general streaming interface; let me know. 17 | 18 | The PNG output is not optimal; it is 20-50% larger than the file 19 | written by a decent optimizing implementation. This library is designed 20 | for source code compactness and simplicity, not optimal image file size 21 | or run-time performance. 22 | 23 | BUILDING: 24 | 25 | You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. 26 | You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace 27 | malloc,realloc,free. 28 | You can define STBIW_MEMMOVE() to replace memmove() 29 | 30 | USAGE: 31 | 32 | There are four functions, one for each image file format: 33 | 34 | int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 35 | int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 36 | int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 37 | int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 38 | 39 | There are also four equivalent functions that use an arbitrary write function. You are 40 | expected to open/close your file-equivalent before and after calling these: 41 | 42 | int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); 43 | int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 44 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 45 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 46 | 47 | where the callback is: 48 | void stbi_write_func(void *context, void *data, int size); 49 | 50 | You can define STBI_WRITE_NO_STDIO to disable the file variant of these 51 | functions, so the library will not use stdio.h at all. However, this will 52 | also disable HDR writing, because it requires stdio for formatted output. 53 | 54 | Each function returns 0 on failure and non-0 on success. 55 | 56 | The functions create an image file defined by the parameters. The image 57 | is a rectangle of pixels stored from left-to-right, top-to-bottom. 58 | Each pixel contains 'comp' channels of data stored interleaved with 8-bits 59 | per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is 60 | monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. 61 | The *data pointer points to the first byte of the top-left-most pixel. 62 | For PNG, "stride_in_bytes" is the distance in bytes from the first byte of 63 | a row of pixels to the first byte of the next row of pixels. 64 | 65 | PNG creates output files with the same number of components as the input. 66 | The BMP format expands Y to RGB in the file format and does not 67 | output alpha. 68 | 69 | PNG supports writing rectangles of data even when the bytes storing rows of 70 | data are not consecutive in memory (e.g. sub-rectangles of a larger image), 71 | by supplying the stride between the beginning of adjacent rows. The other 72 | formats do not. (Thus you cannot write a native-format BMP through the BMP 73 | writer, both because it is in BGR order and because it may have padding 74 | at the end of the line.) 75 | 76 | HDR expects linear float data. Since the format is always 32-bit rgb(e) 77 | data, alpha (if provided) is discarded, and for monochrome data it is 78 | replicated across all three channels. 79 | 80 | TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed 81 | data, set the global variable 'stbi_write_tga_with_rle' to 0. 82 | 83 | CREDITS: 84 | 85 | PNG/BMP/TGA 86 | Sean Barrett 87 | HDR 88 | Baldur Karlsson 89 | TGA monochrome: 90 | Jean-Sebastien Guay 91 | misc enhancements: 92 | Tim Kelsey 93 | TGA RLE 94 | Alan Hickman 95 | initial file IO callback implementation 96 | Emmanuel Julien 97 | bugfixes: 98 | github:Chribba 99 | Guillaume Chereau 100 | github:jry2 101 | github:romigrou 102 | Sergio Gonzalez 103 | Jonas Karlsson 104 | Filip Wasil 105 | Thatcher Ulrich 106 | 107 | LICENSE 108 | 109 | This software is dual-licensed to the public domain and under the following 110 | license: you are granted a perpetual, irrevocable license to copy, modify, 111 | publish, and distribute this file as you see fit. 112 | 113 | */ 114 | 115 | #ifndef INCLUDE_STB_IMAGE_WRITE_H 116 | #define INCLUDE_STB_IMAGE_WRITE_H 117 | 118 | #ifdef __cplusplus 119 | extern "C" { 120 | #endif 121 | 122 | #ifdef STB_IMAGE_WRITE_STATIC 123 | #define STBIWDEF static 124 | #else 125 | #define STBIWDEF extern 126 | extern int stbi_write_tga_with_rle; 127 | #endif 128 | 129 | #ifndef STBI_WRITE_NO_STDIO 130 | STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 131 | STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 132 | STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 133 | STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 134 | #endif 135 | 136 | typedef void stbi_write_func(void *context, void *data, int size); 137 | 138 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); 139 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 140 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 141 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 142 | 143 | #ifdef __cplusplus 144 | } 145 | #endif 146 | 147 | #endif//INCLUDE_STB_IMAGE_WRITE_H 148 | 149 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION 150 | 151 | #ifdef _WIN32 152 | #ifndef _CRT_SECURE_NO_WARNINGS 153 | #define _CRT_SECURE_NO_WARNINGS 154 | #endif 155 | #ifndef _CRT_NONSTDC_NO_DEPRECATE 156 | #define _CRT_NONSTDC_NO_DEPRECATE 157 | #endif 158 | #endif 159 | 160 | #ifndef STBI_WRITE_NO_STDIO 161 | #include 162 | #endif // STBI_WRITE_NO_STDIO 163 | 164 | #include 165 | #include 166 | #include 167 | #include 168 | 169 | #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) 170 | // ok 171 | #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) 172 | // ok 173 | #else 174 | #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." 175 | #endif 176 | 177 | #ifndef STBIW_MALLOC 178 | #define STBIW_MALLOC(sz) malloc(sz) 179 | #define STBIW_REALLOC(p,newsz) realloc(p,newsz) 180 | #define STBIW_FREE(p) free(p) 181 | #endif 182 | 183 | #ifndef STBIW_REALLOC_SIZED 184 | #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) 185 | #endif 186 | 187 | 188 | #ifndef STBIW_MEMMOVE 189 | #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) 190 | #endif 191 | 192 | 193 | #ifndef STBIW_ASSERT 194 | #include 195 | #define STBIW_ASSERT(x) assert(x) 196 | #endif 197 | 198 | #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) 199 | 200 | typedef struct 201 | { 202 | stbi_write_func *func; 203 | void *context; 204 | } stbi__write_context; 205 | 206 | // initialize a callback-based context 207 | static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) 208 | { 209 | s->func = c; 210 | s->context = context; 211 | } 212 | 213 | #ifndef STBI_WRITE_NO_STDIO 214 | 215 | static void stbi__stdio_write(void *context, void *data, int size) 216 | { 217 | fwrite(data,1,size,(FILE*) context); 218 | } 219 | 220 | static int stbi__start_write_file(stbi__write_context *s, const char *filename) 221 | { 222 | FILE *f = fopen(filename, "wb"); 223 | stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); 224 | return f != NULL; 225 | } 226 | 227 | static void stbi__end_write_file(stbi__write_context *s) 228 | { 229 | fclose((FILE *)s->context); 230 | } 231 | 232 | #endif // !STBI_WRITE_NO_STDIO 233 | 234 | typedef unsigned int stbiw_uint32; 235 | typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; 236 | 237 | #ifdef STB_IMAGE_WRITE_STATIC 238 | static int stbi_write_tga_with_rle = 1; 239 | #else 240 | int stbi_write_tga_with_rle = 1; 241 | #endif 242 | 243 | static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) 244 | { 245 | while (*fmt) { 246 | switch (*fmt++) { 247 | case ' ': break; 248 | case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); 249 | s->func(s->context,&x,1); 250 | break; } 251 | case '2': { int x = va_arg(v,int); 252 | unsigned char b[2]; 253 | b[0] = STBIW_UCHAR(x); 254 | b[1] = STBIW_UCHAR(x>>8); 255 | s->func(s->context,b,2); 256 | break; } 257 | case '4': { stbiw_uint32 x = va_arg(v,int); 258 | unsigned char b[4]; 259 | b[0]=STBIW_UCHAR(x); 260 | b[1]=STBIW_UCHAR(x>>8); 261 | b[2]=STBIW_UCHAR(x>>16); 262 | b[3]=STBIW_UCHAR(x>>24); 263 | s->func(s->context,b,4); 264 | break; } 265 | default: 266 | STBIW_ASSERT(0); 267 | return; 268 | } 269 | } 270 | } 271 | 272 | static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) 273 | { 274 | va_list v; 275 | va_start(v, fmt); 276 | stbiw__writefv(s, fmt, v); 277 | va_end(v); 278 | } 279 | 280 | static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) 281 | { 282 | unsigned char arr[3]; 283 | arr[0] = a, arr[1] = b, arr[2] = c; 284 | s->func(s->context, arr, 3); 285 | } 286 | 287 | static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) 288 | { 289 | unsigned char bg[3] = { 255, 0, 255}, px[3]; 290 | int k; 291 | 292 | if (write_alpha < 0) 293 | s->func(s->context, &d[comp - 1], 1); 294 | 295 | switch (comp) { 296 | case 1: 297 | s->func(s->context,d,1); 298 | break; 299 | case 2: 300 | if (expand_mono) 301 | stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp 302 | else 303 | s->func(s->context, d, 1); // monochrome TGA 304 | break; 305 | case 4: 306 | if (!write_alpha) { 307 | // composite against pink background 308 | for (k = 0; k < 3; ++k) 309 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; 310 | stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); 311 | break; 312 | } 313 | /* FALLTHROUGH */ 314 | case 3: 315 | stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); 316 | break; 317 | } 318 | if (write_alpha > 0) 319 | s->func(s->context, &d[comp - 1], 1); 320 | } 321 | 322 | static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) 323 | { 324 | stbiw_uint32 zero = 0; 325 | int i,j, j_end; 326 | 327 | if (y <= 0) 328 | return; 329 | 330 | if (vdir < 0) 331 | j_end = -1, j = y-1; 332 | else 333 | j_end = y, j = 0; 334 | 335 | for (; j != j_end; j += vdir) { 336 | for (i=0; i < x; ++i) { 337 | unsigned char *d = (unsigned char *) data + (j*x+i)*comp; 338 | stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); 339 | } 340 | s->func(s->context, &zero, scanline_pad); 341 | } 342 | } 343 | 344 | static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) 345 | { 346 | if (y < 0 || x < 0) { 347 | return 0; 348 | } else { 349 | va_list v; 350 | va_start(v, fmt); 351 | stbiw__writefv(s, fmt, v); 352 | va_end(v); 353 | stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); 354 | return 1; 355 | } 356 | } 357 | 358 | static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) 359 | { 360 | int pad = (-x*3) & 3; 361 | return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, 362 | "11 4 22 4" "4 44 22 444444", 363 | 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 364 | 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header 365 | } 366 | 367 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 368 | { 369 | stbi__write_context s; 370 | stbi__start_write_callbacks(&s, func, context); 371 | return stbi_write_bmp_core(&s, x, y, comp, data); 372 | } 373 | 374 | #ifndef STBI_WRITE_NO_STDIO 375 | STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) 376 | { 377 | stbi__write_context s; 378 | if (stbi__start_write_file(&s,filename)) { 379 | int r = stbi_write_bmp_core(&s, x, y, comp, data); 380 | stbi__end_write_file(&s); 381 | return r; 382 | } else 383 | return 0; 384 | } 385 | #endif //!STBI_WRITE_NO_STDIO 386 | 387 | static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) 388 | { 389 | int has_alpha = (comp == 2 || comp == 4); 390 | int colorbytes = has_alpha ? comp-1 : comp; 391 | int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 392 | 393 | if (y < 0 || x < 0) 394 | return 0; 395 | 396 | if (!stbi_write_tga_with_rle) { 397 | return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, 398 | "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); 399 | } else { 400 | int i,j,k; 401 | 402 | stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); 403 | 404 | for (j = y - 1; j >= 0; --j) { 405 | unsigned char *row = (unsigned char *) data + j * x * comp; 406 | int len; 407 | 408 | for (i = 0; i < x; i += len) { 409 | unsigned char *begin = row + i * comp; 410 | int diff = 1; 411 | len = 1; 412 | 413 | if (i < x - 1) { 414 | ++len; 415 | diff = memcmp(begin, row + (i + 1) * comp, comp); 416 | if (diff) { 417 | const unsigned char *prev = begin; 418 | for (k = i + 2; k < x && len < 128; ++k) { 419 | if (memcmp(prev, row + k * comp, comp)) { 420 | prev += comp; 421 | ++len; 422 | } else { 423 | --len; 424 | break; 425 | } 426 | } 427 | } else { 428 | for (k = i + 2; k < x && len < 128; ++k) { 429 | if (!memcmp(begin, row + k * comp, comp)) { 430 | ++len; 431 | } else { 432 | break; 433 | } 434 | } 435 | } 436 | } 437 | 438 | if (diff) { 439 | unsigned char header = STBIW_UCHAR(len - 1); 440 | s->func(s->context, &header, 1); 441 | for (k = 0; k < len; ++k) { 442 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); 443 | } 444 | } else { 445 | unsigned char header = STBIW_UCHAR(len - 129); 446 | s->func(s->context, &header, 1); 447 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); 448 | } 449 | } 450 | } 451 | } 452 | return 1; 453 | } 454 | 455 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 456 | { 457 | stbi__write_context s; 458 | stbi__start_write_callbacks(&s, func, context); 459 | return stbi_write_tga_core(&s, x, y, comp, (void *) data); 460 | } 461 | 462 | #ifndef STBI_WRITE_NO_STDIO 463 | int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) 464 | { 465 | stbi__write_context s; 466 | if (stbi__start_write_file(&s,filename)) { 467 | int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); 468 | stbi__end_write_file(&s); 469 | return r; 470 | } else 471 | return 0; 472 | } 473 | #endif 474 | 475 | // ************************************************************************************************* 476 | // Radiance RGBE HDR writer 477 | // by Baldur Karlsson 478 | #ifndef STBI_WRITE_NO_STDIO 479 | 480 | #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) 481 | 482 | void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) 483 | { 484 | int exponent; 485 | float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); 486 | 487 | if (maxcomp < 1e-32f) { 488 | rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 489 | } else { 490 | float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; 491 | 492 | rgbe[0] = (unsigned char)(linear[0] * normalize); 493 | rgbe[1] = (unsigned char)(linear[1] * normalize); 494 | rgbe[2] = (unsigned char)(linear[2] * normalize); 495 | rgbe[3] = (unsigned char)(exponent + 128); 496 | } 497 | } 498 | 499 | void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) 500 | { 501 | unsigned char lengthbyte = STBIW_UCHAR(length+128); 502 | STBIW_ASSERT(length+128 <= 255); 503 | s->func(s->context, &lengthbyte, 1); 504 | s->func(s->context, &databyte, 1); 505 | } 506 | 507 | void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) 508 | { 509 | unsigned char lengthbyte = STBIW_UCHAR(length); 510 | STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code 511 | s->func(s->context, &lengthbyte, 1); 512 | s->func(s->context, data, length); 513 | } 514 | 515 | void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) 516 | { 517 | unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; 518 | unsigned char rgbe[4]; 519 | float linear[3]; 520 | int x; 521 | 522 | scanlineheader[2] = (width&0xff00)>>8; 523 | scanlineheader[3] = (width&0x00ff); 524 | 525 | /* skip RLE for images too small or large */ 526 | if (width < 8 || width >= 32768) { 527 | for (x=0; x < width; x++) { 528 | switch (ncomp) { 529 | case 4: /* fallthrough */ 530 | case 3: linear[2] = scanline[x*ncomp + 2]; 531 | linear[1] = scanline[x*ncomp + 1]; 532 | linear[0] = scanline[x*ncomp + 0]; 533 | break; 534 | default: 535 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 536 | break; 537 | } 538 | stbiw__linear_to_rgbe(rgbe, linear); 539 | s->func(s->context, rgbe, 4); 540 | } 541 | } else { 542 | int c,r; 543 | /* encode into scratch buffer */ 544 | for (x=0; x < width; x++) { 545 | switch(ncomp) { 546 | case 4: /* fallthrough */ 547 | case 3: linear[2] = scanline[x*ncomp + 2]; 548 | linear[1] = scanline[x*ncomp + 1]; 549 | linear[0] = scanline[x*ncomp + 0]; 550 | break; 551 | default: 552 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 553 | break; 554 | } 555 | stbiw__linear_to_rgbe(rgbe, linear); 556 | scratch[x + width*0] = rgbe[0]; 557 | scratch[x + width*1] = rgbe[1]; 558 | scratch[x + width*2] = rgbe[2]; 559 | scratch[x + width*3] = rgbe[3]; 560 | } 561 | 562 | s->func(s->context, scanlineheader, 4); 563 | 564 | /* RLE each component separately */ 565 | for (c=0; c < 4; c++) { 566 | unsigned char *comp = &scratch[width*c]; 567 | 568 | x = 0; 569 | while (x < width) { 570 | // find first run 571 | r = x; 572 | while (r+2 < width) { 573 | if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) 574 | break; 575 | ++r; 576 | } 577 | if (r+2 >= width) 578 | r = width; 579 | // dump up to first run 580 | while (x < r) { 581 | int len = r-x; 582 | if (len > 128) len = 128; 583 | stbiw__write_dump_data(s, len, &comp[x]); 584 | x += len; 585 | } 586 | // if there's a run, output it 587 | if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd 588 | // find next byte after run 589 | while (r < width && comp[r] == comp[x]) 590 | ++r; 591 | // output run up to r 592 | while (x < r) { 593 | int len = r-x; 594 | if (len > 127) len = 127; 595 | stbiw__write_run_data(s, len, comp[x]); 596 | x += len; 597 | } 598 | } 599 | } 600 | } 601 | } 602 | } 603 | 604 | static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) 605 | { 606 | if (y <= 0 || x <= 0 || data == NULL) 607 | return 0; 608 | else { 609 | // Each component is stored separately. Allocate scratch space for full output scanline. 610 | unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); 611 | int i, len; 612 | char buffer[128]; 613 | char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; 614 | s->func(s->context, header, sizeof(header)-1); 615 | 616 | len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 617 | s->func(s->context, buffer, len); 618 | 619 | for(i=0; i < y; i++) 620 | stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); 621 | STBIW_FREE(scratch); 622 | return 1; 623 | } 624 | } 625 | 626 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) 627 | { 628 | stbi__write_context s; 629 | stbi__start_write_callbacks(&s, func, context); 630 | return stbi_write_hdr_core(&s, x, y, comp, (float *) data); 631 | } 632 | 633 | int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) 634 | { 635 | stbi__write_context s; 636 | if (stbi__start_write_file(&s,filename)) { 637 | int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); 638 | stbi__end_write_file(&s); 639 | return r; 640 | } else 641 | return 0; 642 | } 643 | #endif // STBI_WRITE_NO_STDIO 644 | 645 | 646 | ////////////////////////////////////////////////////////////////////////////// 647 | // 648 | // PNG writer 649 | // 650 | 651 | // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() 652 | #define stbiw__sbraw(a) ((int *) (a) - 2) 653 | #define stbiw__sbm(a) stbiw__sbraw(a)[0] 654 | #define stbiw__sbn(a) stbiw__sbraw(a)[1] 655 | 656 | #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) 657 | #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) 658 | #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) 659 | 660 | #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) 661 | #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) 662 | #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) 663 | 664 | static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) 665 | { 666 | int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; 667 | void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); 668 | STBIW_ASSERT(p); 669 | if (p) { 670 | if (!*arr) ((int *) p)[1] = 0; 671 | *arr = (void *) ((int *) p + 2); 672 | stbiw__sbm(*arr) = m; 673 | } 674 | return *arr; 675 | } 676 | 677 | static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) 678 | { 679 | while (*bitcount >= 8) { 680 | stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); 681 | *bitbuffer >>= 8; 682 | *bitcount -= 8; 683 | } 684 | return data; 685 | } 686 | 687 | static int stbiw__zlib_bitrev(int code, int codebits) 688 | { 689 | int res=0; 690 | while (codebits--) { 691 | res = (res << 1) | (code & 1); 692 | code >>= 1; 693 | } 694 | return res; 695 | } 696 | 697 | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) 698 | { 699 | int i; 700 | for (i=0; i < limit && i < 258; ++i) 701 | if (a[i] != b[i]) break; 702 | return i; 703 | } 704 | 705 | static unsigned int stbiw__zhash(unsigned char *data) 706 | { 707 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); 708 | hash ^= hash << 3; 709 | hash += hash >> 5; 710 | hash ^= hash << 4; 711 | hash += hash >> 17; 712 | hash ^= hash << 25; 713 | hash += hash >> 6; 714 | return hash; 715 | } 716 | 717 | #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) 718 | #define stbiw__zlib_add(code,codebits) \ 719 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) 720 | #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) 721 | // default huffman tables 722 | #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) 723 | #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) 724 | #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) 725 | #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) 726 | #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) 727 | #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) 728 | 729 | #define stbiw__ZHASH 16384 730 | 731 | unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) 732 | { 733 | static unsigned short lengthc[] = { 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, 259 }; 734 | static unsigned char lengtheb[]= { 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 }; 735 | static unsigned short distc[] = { 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, 32768 }; 736 | static unsigned char disteb[] = { 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 }; 737 | unsigned int bitbuf=0; 738 | int i,j, bitcount=0; 739 | unsigned char *out = NULL; 740 | unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); 741 | if (quality < 5) quality = 5; 742 | 743 | stbiw__sbpush(out, 0x78); // DEFLATE 32K window 744 | stbiw__sbpush(out, 0x5e); // FLEVEL = 1 745 | stbiw__zlib_add(1,1); // BFINAL = 1 746 | stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman 747 | 748 | for (i=0; i < stbiw__ZHASH; ++i) 749 | hash_table[i] = NULL; 750 | 751 | i=0; 752 | while (i < data_len-3) { 753 | // hash next 3 bytes of data to be compressed 754 | int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; 755 | unsigned char *bestloc = 0; 756 | unsigned char **hlist = hash_table[h]; 757 | int n = stbiw__sbcount(hlist); 758 | for (j=0; j < n; ++j) { 759 | if (hlist[j]-data > i-32768) { // if entry lies within window 760 | int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); 761 | if (d >= best) best=d,bestloc=hlist[j]; 762 | } 763 | } 764 | // when hash table entry is too long, delete half the entries 765 | if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { 766 | STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); 767 | stbiw__sbn(hash_table[h]) = quality; 768 | } 769 | stbiw__sbpush(hash_table[h],data+i); 770 | 771 | if (bestloc) { 772 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal 773 | h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); 774 | hlist = hash_table[h]; 775 | n = stbiw__sbcount(hlist); 776 | for (j=0; j < n; ++j) { 777 | if (hlist[j]-data > i-32767) { 778 | int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); 779 | if (e > best) { // if next match is better, bail on current match 780 | bestloc = NULL; 781 | break; 782 | } 783 | } 784 | } 785 | } 786 | 787 | if (bestloc) { 788 | int d = (int) (data+i - bestloc); // distance back 789 | STBIW_ASSERT(d <= 32767 && best <= 258); 790 | for (j=0; best > lengthc[j+1]-1; ++j); 791 | stbiw__zlib_huff(j+257); 792 | if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); 793 | for (j=0; d > distc[j+1]-1; ++j); 794 | stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); 795 | if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); 796 | i += best; 797 | } else { 798 | stbiw__zlib_huffb(data[i]); 799 | ++i; 800 | } 801 | } 802 | // write out final bytes 803 | for (;i < data_len; ++i) 804 | stbiw__zlib_huffb(data[i]); 805 | stbiw__zlib_huff(256); // end of block 806 | // pad with 0 bits to byte boundary 807 | while (bitcount) 808 | stbiw__zlib_add(0,1); 809 | 810 | for (i=0; i < stbiw__ZHASH; ++i) 811 | (void) stbiw__sbfree(hash_table[i]); 812 | STBIW_FREE(hash_table); 813 | 814 | { 815 | // compute adler32 on input 816 | unsigned int s1=1, s2=0; 817 | int blocklen = (int) (data_len % 5552); 818 | j=0; 819 | while (j < data_len) { 820 | for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; 821 | s1 %= 65521, s2 %= 65521; 822 | j += blocklen; 823 | blocklen = 5552; 824 | } 825 | stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); 826 | stbiw__sbpush(out, STBIW_UCHAR(s2)); 827 | stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); 828 | stbiw__sbpush(out, STBIW_UCHAR(s1)); 829 | } 830 | *out_len = stbiw__sbn(out); 831 | // make returned pointer freeable 832 | STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); 833 | return (unsigned char *) stbiw__sbraw(out); 834 | } 835 | 836 | static unsigned int stbiw__crc32(unsigned char *buffer, int len) 837 | { 838 | static unsigned int crc_table[256] = 839 | { 840 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 841 | 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 842 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 843 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 844 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 845 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 846 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 847 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 848 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 849 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 850 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 851 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 852 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 853 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 854 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 855 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 856 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 857 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 858 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 859 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 860 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 861 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 862 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 863 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 864 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 865 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 866 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 867 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 868 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 869 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 870 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 871 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 872 | }; 873 | 874 | unsigned int crc = ~0u; 875 | int i; 876 | for (i=0; i < len; ++i) 877 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; 878 | return ~crc; 879 | } 880 | 881 | #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) 882 | #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); 883 | #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) 884 | 885 | static void stbiw__wpcrc(unsigned char **data, int len) 886 | { 887 | unsigned int crc = stbiw__crc32(*data - len - 4, len+4); 888 | stbiw__wp32(*data, crc); 889 | } 890 | 891 | static unsigned char stbiw__paeth(int a, int b, int c) 892 | { 893 | int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); 894 | if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); 895 | if (pb <= pc) return STBIW_UCHAR(b); 896 | return STBIW_UCHAR(c); 897 | } 898 | 899 | unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) 900 | { 901 | int ctype[5] = { -1, 0, 4, 2, 6 }; 902 | unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; 903 | unsigned char *out,*o, *filt, *zlib; 904 | signed char *line_buffer; 905 | int i,j,k,p,zlen; 906 | 907 | if (stride_bytes == 0) 908 | stride_bytes = x * n; 909 | 910 | filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; 911 | line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } 912 | for (j=0; j < y; ++j) { 913 | static int mapping[] = { 0,1,2,3,4 }; 914 | static int firstmap[] = { 0,1,0,5,6 }; 915 | int *mymap = j ? mapping : firstmap; 916 | int best = 0, bestval = 0x7fffffff; 917 | for (p=0; p < 2; ++p) { 918 | for (k= p?best:0; k < 5; ++k) { 919 | int type = mymap[k],est=0; 920 | unsigned char *z = pixels + stride_bytes*j; 921 | for (i=0; i < n; ++i) 922 | switch (type) { 923 | case 0: line_buffer[i] = z[i]; break; 924 | case 1: line_buffer[i] = z[i]; break; 925 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 926 | case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; 927 | case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; 928 | case 5: line_buffer[i] = z[i]; break; 929 | case 6: line_buffer[i] = z[i]; break; 930 | } 931 | for (i=n; i < x*n; ++i) { 932 | switch (type) { 933 | case 0: line_buffer[i] = z[i]; break; 934 | case 1: line_buffer[i] = z[i] - z[i-n]; break; 935 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 936 | case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; 937 | case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; 938 | case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; 939 | case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; 940 | } 941 | } 942 | if (p) break; 943 | for (i=0; i < x*n; ++i) 944 | est += abs((signed char) line_buffer[i]); 945 | if (est < bestval) { bestval = est; best = k; } 946 | } 947 | } 948 | // when we get here, best contains the filter type, and line_buffer contains the data 949 | filt[j*(x*n+1)] = (unsigned char) best; 950 | STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); 951 | } 952 | STBIW_FREE(line_buffer); 953 | zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory 954 | STBIW_FREE(filt); 955 | if (!zlib) return 0; 956 | 957 | // each tag requires 12 bytes of overhead 958 | out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); 959 | if (!out) return 0; 960 | *out_len = 8 + 12+13 + 12+zlen + 12; 961 | 962 | o=out; 963 | STBIW_MEMMOVE(o,sig,8); o+= 8; 964 | stbiw__wp32(o, 13); // header length 965 | stbiw__wptag(o, "IHDR"); 966 | stbiw__wp32(o, x); 967 | stbiw__wp32(o, y); 968 | *o++ = 8; 969 | *o++ = STBIW_UCHAR(ctype[n]); 970 | *o++ = 0; 971 | *o++ = 0; 972 | *o++ = 0; 973 | stbiw__wpcrc(&o,13); 974 | 975 | stbiw__wp32(o, zlen); 976 | stbiw__wptag(o, "IDAT"); 977 | STBIW_MEMMOVE(o, zlib, zlen); 978 | o += zlen; 979 | STBIW_FREE(zlib); 980 | stbiw__wpcrc(&o, zlen); 981 | 982 | stbiw__wp32(o,0); 983 | stbiw__wptag(o, "IEND"); 984 | stbiw__wpcrc(&o,0); 985 | 986 | STBIW_ASSERT(o == out + *out_len); 987 | 988 | return out; 989 | } 990 | 991 | #ifndef STBI_WRITE_NO_STDIO 992 | STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) 993 | { 994 | FILE *f; 995 | int len; 996 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 997 | if (png == NULL) return 0; 998 | f = fopen(filename, "wb"); 999 | if (!f) { STBIW_FREE(png); return 0; } 1000 | fwrite(png, 1, len, f); 1001 | fclose(f); 1002 | STBIW_FREE(png); 1003 | return 1; 1004 | } 1005 | #endif 1006 | 1007 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) 1008 | { 1009 | int len; 1010 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 1011 | if (png == NULL) return 0; 1012 | func(context, png, len); 1013 | STBIW_FREE(png); 1014 | return 1; 1015 | } 1016 | 1017 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION 1018 | 1019 | /* Revision history 1020 | 1.02 (2016-04-02) 1021 | avoid allocating large structures on the stack 1022 | 1.01 (2016-01-16) 1023 | STBIW_REALLOC_SIZED: support allocators with no realloc support 1024 | avoid race-condition in crc initialization 1025 | minor compile issues 1026 | 1.00 (2015-09-14) 1027 | installable file IO function 1028 | 0.99 (2015-09-13) 1029 | warning fixes; TGA rle support 1030 | 0.98 (2015-04-08) 1031 | added STBIW_MALLOC, STBIW_ASSERT etc 1032 | 0.97 (2015-01-18) 1033 | fixed HDR asserts, rewrote HDR rle logic 1034 | 0.96 (2015-01-17) 1035 | add HDR output 1036 | fix monochrome BMP 1037 | 0.95 (2014-08-17) 1038 | add monochrome TGA output 1039 | 0.94 (2014-05-31) 1040 | rename private functions to avoid conflicts with stb_image.h 1041 | 0.93 (2014-05-27) 1042 | warning fixes 1043 | 0.92 (2010-08-01) 1044 | casts to unsigned char to fix warnings 1045 | 0.91 (2010-07-17) 1046 | first public release 1047 | 0.90 first internal release 1048 | */ 1049 | --------------------------------------------------------------------------------