Compare commits
18 Commits
6b6465b6f3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 92912a321c | |||
| e04f1cf291 | |||
| efa6e289b6 | |||
| 596af80622 | |||
| f4fad2c1ac | |||
| e9ae017e9b | |||
| cddfa30cfe | |||
| 5ca02ed9e2 | |||
| 9f2dab344d | |||
| 402cdd43da | |||
| fc66ce2fd3 | |||
| 46f5fab55e | |||
| e55601b5a6 | |||
| 979dab81b1 | |||
| 26edfcbe89 | |||
| 46c428b13a | |||
| 447114e38d | |||
| 7978606a52 |
1
.gitignore
vendored
@@ -3,3 +3,4 @@ result
|
||||
.cache
|
||||
.direnv
|
||||
.clangd
|
||||
screenshot*
|
||||
|
||||
BIN
assets/cubemap.png
Normal file
|
After Width: | Height: | Size: 17 MiB |
@@ -23,6 +23,7 @@
|
||||
pkg-config
|
||||
glslang
|
||||
shaderc
|
||||
wayland-scanner
|
||||
];
|
||||
buildInputs = with pkgs; [
|
||||
vulkan-loader
|
||||
|
||||
43
meson.build
@@ -1,4 +1,4 @@
|
||||
project('vr-compositor', 'cpp',
|
||||
project('vr-compositor', 'c', 'cpp',
|
||||
version: '0.1',
|
||||
default_options: [
|
||||
'cpp_std=c++26',
|
||||
@@ -17,14 +17,32 @@ fastgltf_opts.set_override_option('werror', 'false')
|
||||
fastgltf = cmake.subproject('fastgltf', options: fastgltf_opts)
|
||||
|
||||
cc = meson.get_compiler('cpp')
|
||||
wl_mod = import('wayland')
|
||||
|
||||
wayland_dep = dependency('wayland-server', include_type: 'system')
|
||||
wayland_client_dep = dependency('wayland-client', include_type: 'system')
|
||||
vulkan_dep = dependency('vulkan', include_type: 'system')
|
||||
openxr_dep = dependency('openxr', include_type: 'system')
|
||||
zlib_dep = dependency('zlib', include_type: 'system')
|
||||
sdl3_dep = dependency('sdl3', include_type: 'system')
|
||||
libinput_dep = dependency('libinput', include_type: 'system')
|
||||
libudev_dep = dependency('libudev', include_type: 'system')
|
||||
wayland_protocol = wl_mod.scan_xml(
|
||||
'protocols/wayland.xml',
|
||||
client: false,
|
||||
server: true,
|
||||
)
|
||||
wayland_protocol_source = wayland_protocol[0]
|
||||
wayland_server_header = wayland_protocol[1]
|
||||
|
||||
xdg_shell_protocol = wl_mod.scan_xml(
|
||||
'protocols/xdg-shell.xml',
|
||||
client: true,
|
||||
server: true,
|
||||
)
|
||||
xdg_shell_protocol_source = xdg_shell_protocol[0]
|
||||
xdg_shell_client_header = xdg_shell_protocol[1]
|
||||
xdg_shell_server_header = xdg_shell_protocol[2]
|
||||
imgui_src = files(
|
||||
'thirdparty/imgui/imgui.cpp',
|
||||
'thirdparty/imgui/imgui_draw.cpp',
|
||||
@@ -95,6 +113,7 @@ add_project_arguments(
|
||||
'-Wno-zero-as-null-pointer-constant',
|
||||
'-Wno-unused-macros',
|
||||
'-Wno-reserved-macro-identifier',
|
||||
'-Wno-reserved-identifier',
|
||||
'-Wno-suggest-override',
|
||||
'-Wno-macro-redefined',
|
||||
'-DVULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE',
|
||||
@@ -159,11 +178,25 @@ exe = executable('vr-compositor',
|
||||
'src/Pipeline.cpp',
|
||||
'src/Loader.cpp',
|
||||
'src/DescriptorWriter.cpp',
|
||||
'src/CPUTexture.cpp',
|
||||
'src/wayland/WaylandServer.cpp',
|
||||
'src/wayland/Surface.cpp',
|
||||
'src/wayland/Shm.cpp',
|
||||
'src/wayland/protocols/CompositorProtocol.cpp',
|
||||
'src/wayland/protocols/ShmProtocol.cpp',
|
||||
'src/wayland/protocols/XdgShellProtocol.cpp',
|
||||
'src/Skybox.cpp',
|
||||
'src/VulkanRenderer.cpp',
|
||||
'src/Application.cpp',
|
||||
wayland_protocol_source,
|
||||
xdg_shell_protocol_source,
|
||||
wayland_server_header,
|
||||
xdg_shell_server_header,
|
||||
],
|
||||
c_args: ['-Wno-missing-variable-declarations'],
|
||||
include_directories: [
|
||||
vkbootstrap_inc,
|
||||
include_directories('.'),
|
||||
imgui_inc,
|
||||
'thirdparty/smath/include'
|
||||
],
|
||||
@@ -173,3 +206,11 @@ exe = executable('vr-compositor',
|
||||
'--embed-dir=' + join_paths(meson.project_build_root(), 'shaders'),
|
||||
],
|
||||
)
|
||||
|
||||
executable('shm-life',
|
||||
'tools/shm_life.cpp',
|
||||
dependencies: [wayland_client_dep],
|
||||
sources: [xdg_shell_protocol_source, xdg_shell_client_header],
|
||||
c_args: ['-Wno-missing-variable-declarations'],
|
||||
cpp_args: ['-Wno-cast-qual'],
|
||||
)
|
||||
|
||||
3314
protocols/wayland.xml
Normal file
1418
protocols/xdg-shell.xml
Normal file
@@ -19,6 +19,8 @@ shader_sources = files(
|
||||
'triangle_mesh.frag',
|
||||
'triangle_mesh.vert',
|
||||
'tex_image.frag',
|
||||
'skybox.frag',
|
||||
'skybox.vert',
|
||||
)
|
||||
|
||||
spirv_shaders = []
|
||||
|
||||
12
shaders/skybox.frag
Normal file
@@ -0,0 +1,12 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec3 in_dir;
|
||||
|
||||
layout (location = 0) out vec4 out_frag_color;
|
||||
|
||||
layout (set = 0, binding = 0) uniform samplerCube environment_map;
|
||||
|
||||
void main() {
|
||||
vec3 dir = normalize(in_dir);
|
||||
out_frag_color = vec4(texture(environment_map, dir).rgb, 1.0f);
|
||||
}
|
||||
14
shaders/skybox.vert
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec3 in_position;
|
||||
layout (location = 0) out vec3 out_dir;
|
||||
|
||||
layout(push_constant) uniform constants {
|
||||
mat4 mvp;
|
||||
} PushConstants;
|
||||
|
||||
void main() {
|
||||
out_dir = in_position;
|
||||
vec4 pos = PushConstants.mvp * vec4(in_position, 1.0f);
|
||||
gl_Position = vec4(pos.xy, pos.w, pos.w);
|
||||
}
|
||||
1903
src/Application.cpp
@@ -1,14 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <imgui.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
#include "smath.hpp"
|
||||
|
||||
#include "Loader.h"
|
||||
#include "Logger.h"
|
||||
#include "Skybox.h"
|
||||
#include "Types.h"
|
||||
#include "wayland/Display.h"
|
||||
|
||||
struct libinput;
|
||||
struct libinput_event_keyboard;
|
||||
@@ -18,36 +28,80 @@ struct udev;
|
||||
namespace Lunar {
|
||||
|
||||
struct VulkanRenderer;
|
||||
struct OpenXrState;
|
||||
namespace Wayland {
|
||||
struct WaylandServer;
|
||||
}
|
||||
|
||||
struct Application {
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
auto run() -> void;
|
||||
auto binary_directory() const -> std::filesystem::path;
|
||||
|
||||
auto mouse_captured(bool new_state) -> void;
|
||||
auto mouse_captured() const -> bool { return m_mouse_captured; }
|
||||
auto toggle_mouse_captured() -> void { mouse_captured(!m_mouse_captured); }
|
||||
auto is_key_down(uint32_t key) const -> bool;
|
||||
auto is_key_up(uint32_t key) const -> bool;
|
||||
auto is_key_pressed(uint32_t key) const -> bool;
|
||||
auto is_key_released(uint32_t key) const -> bool;
|
||||
|
||||
static auto the() -> Application &;
|
||||
|
||||
private:
|
||||
enum class Backend {
|
||||
SDL,
|
||||
KMS,
|
||||
};
|
||||
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
auto init_input() -> void;
|
||||
auto init_test_meshes() -> void;
|
||||
auto init_wayland() -> void;
|
||||
auto asset_directory() -> std::filesystem::path;
|
||||
auto shutdown_input() -> void;
|
||||
auto process_libinput_events() -> void;
|
||||
auto handle_keyboard_event(libinput_event_keyboard *event) -> void;
|
||||
auto handle_pointer_motion(libinput_event_pointer *event) -> void;
|
||||
auto handle_pointer_button(libinput_event_pointer *event) -> void;
|
||||
auto handle_pointer_axis(libinput_event_pointer *event) -> void;
|
||||
auto handle_pointer_frame() -> void;
|
||||
auto handle_pointer_end_frame() -> void;
|
||||
auto handle_pointer_cancel() -> void;
|
||||
auto handle_keyboard_key(std::optional<uint32_t> key, bool pressed) -> void;
|
||||
auto clamp_mouse_to_window(int width, int height) -> void;
|
||||
auto init_openxr() -> void;
|
||||
auto init_openxr_session() -> void;
|
||||
auto shutdown_openxr() -> void;
|
||||
auto poll_openxr_events() -> void;
|
||||
auto render_openxr_frame(
|
||||
std::function<void(VulkanRenderer::GL &)> const &record,
|
||||
float dt_seconds) -> bool;
|
||||
auto update_camera_from_xr_view(XrView const &view) -> void;
|
||||
auto update_hands(XrTime display_time) -> void;
|
||||
auto render_hands(
|
||||
VulkanRenderer::GL &gl, smath::Mat4 const &view_projection) -> void;
|
||||
|
||||
SDL_Window *m_window { nullptr };
|
||||
Backend m_backend { Backend::SDL };
|
||||
Logger m_logger { "Lunar" };
|
||||
std::unique_ptr<VulkanRenderer> m_renderer;
|
||||
Skybox m_skybox;
|
||||
std::vector<std::shared_ptr<Mesh>> m_test_meshes;
|
||||
|
||||
udev *m_udev { nullptr };
|
||||
libinput *m_libinput { nullptr };
|
||||
|
||||
std::unique_ptr<OpenXrState> m_openxr {};
|
||||
std::unique_ptr<Wayland::WaylandServer> m_wayland {};
|
||||
|
||||
bool m_running { true };
|
||||
bool m_mouse_captured { false };
|
||||
bool m_show_imgui { false };
|
||||
bool m_window_focused { true };
|
||||
int m_ctrl_pressed_count { 0 };
|
||||
std::uint32_t m_screenshot_index { 0 };
|
||||
|
||||
double m_mouse_x { 0.0 };
|
||||
double m_mouse_y { 0.0 };
|
||||
@@ -56,9 +110,17 @@ private:
|
||||
float m_mouse_sensitivity { 0.001f };
|
||||
|
||||
std::array<bool, KEY_MAX + 1> m_key_state {};
|
||||
std::array<bool, KEY_MAX + 1> m_key_state_previous {};
|
||||
|
||||
Camera m_camera;
|
||||
PolarCoordinate m_cursor;
|
||||
|
||||
static inline std::array<smath::Vec3, XR_HAND_JOINT_COUNT_EXT>
|
||||
m_left_joints {};
|
||||
static inline std::array<smath::Vec3, XR_HAND_JOINT_COUNT_EXT>
|
||||
m_right_joints {};
|
||||
static inline bool m_left_hand_valid { false };
|
||||
static inline bool m_right_hand_valid { false };
|
||||
};
|
||||
|
||||
} // namespace Lunar
|
||||
|
||||
88
src/CPUTexture.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "CPUTexture.h"
|
||||
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wreserved-identifier"
|
||||
# pragma clang diagnostic ignored "-Wcast-qual"
|
||||
# pragma clang diagnostic ignored "-Wimplicit-fallthrough"
|
||||
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||
# pragma clang diagnostic ignored "-Wused-but-marked-unused"
|
||||
# pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
# pragma clang diagnostic ignored "-Wextra-semi-stmt"
|
||||
# pragma clang diagnostic ignored "-Wimplicit-int-conversion"
|
||||
# pragma clang diagnostic ignored "-Wsign-conversion"
|
||||
# pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
||||
# pragma clang diagnostic ignored "-Wconversion"
|
||||
# pragma clang diagnostic ignored "-Wcomma"
|
||||
# pragma clang diagnostic ignored "-Wdouble-promotion"
|
||||
# pragma clang diagnostic ignored "-Wimplicit-float-conversion"
|
||||
# pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
# pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
|
||||
# pragma clang diagnostic ignored "-Wsign-compare"
|
||||
# pragma clang diagnostic ignored "-Wfloat-equal"
|
||||
# pragma clang diagnostic ignored "-Wpacked"
|
||||
# pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
# pragma clang diagnostic ignored "-Wexit-time-destructors"
|
||||
# pragma clang diagnostic ignored "-Wglobal-constructors"
|
||||
# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||
# pragma clang diagnostic ignored "-Wcast-align"
|
||||
# pragma clang diagnostic ignored "-Wcast-qual"
|
||||
# pragma clang diagnostic ignored "-Wshadow"
|
||||
# pragma clang diagnostic ignored "-Wnewline-eof"
|
||||
# pragma clang diagnostic ignored "-Wformat-nonliteral"
|
||||
# pragma clang diagnostic ignored "-Wswitch-default"
|
||||
# pragma clang diagnostic ignored "-Wswitch-enum"
|
||||
# pragma clang diagnostic ignored "-Wcovered-switch-default"
|
||||
# pragma clang diagnostic ignored "-Wdocumentation"
|
||||
# pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
||||
# pragma clang diagnostic ignored "-Wextra-semi"
|
||||
# pragma clang diagnostic ignored "-Wundef"
|
||||
# pragma clang diagnostic ignored "-Wreserved-macro-identifier"
|
||||
# pragma clang diagnostic ignored "-Wc++98-compat"
|
||||
# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
|
||||
# pragma clang diagnostic ignored "-Wweak-vtables"
|
||||
# pragma clang diagnostic ignored "-Wswitch"
|
||||
# pragma clang diagnostic ignored "-Wunused-macros"
|
||||
# pragma clang diagnostic ignored "-Wextra"
|
||||
#endif
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "../thirdparty/stb/stb_image.h"
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
CPUTexture::CPUTexture(std::filesystem::path const &path)
|
||||
{
|
||||
int width_out { 0 };
|
||||
int height_out { 0 };
|
||||
int channels_out { 0 };
|
||||
stbi_uc *data { stbi_load(path.string().c_str(), &width_out, &height_out,
|
||||
&channels_out, STBI_rgb_alpha) };
|
||||
if (!data) {
|
||||
throw std::runtime_error(
|
||||
std::format("Failed to load texture: {}", path.string()));
|
||||
}
|
||||
|
||||
width = static_cast<uint32_t>(width_out);
|
||||
height = static_cast<uint32_t>(height_out);
|
||||
format = vk::Format::eR8G8B8A8Unorm;
|
||||
pixels.assign(data, data + (width * height * 4));
|
||||
stbi_image_free(data);
|
||||
}
|
||||
|
||||
CPUTexture::CPUTexture(std::vector<uint8_t> pixels, uint32_t width,
|
||||
uint32_t height, vk::Format format)
|
||||
: pixels(std::move(pixels))
|
||||
, width(width)
|
||||
, height(height)
|
||||
, format(format)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Lunar
|
||||
22
src/CPUTexture.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
struct CPUTexture {
|
||||
std::vector<uint8_t> pixels;
|
||||
uint32_t width { 0 };
|
||||
uint32_t height { 0 };
|
||||
vk::Format format { vk::Format::eR8G8B8A8Unorm };
|
||||
|
||||
explicit CPUTexture(std::filesystem::path const &path);
|
||||
CPUTexture(std::vector<uint8_t> pixels, uint32_t width, uint32_t height,
|
||||
vk::Format format);
|
||||
};
|
||||
|
||||
} // namespace Lunar
|
||||
@@ -78,7 +78,7 @@ auto DescriptorAllocatorGrowable::destroy_pools(VkDevice dev) -> void
|
||||
auto DescriptorAllocatorGrowable::allocate(Logger &logger, VkDevice dev,
|
||||
VkDescriptorSetLayout layout, void *p_next) -> VkDescriptorSet
|
||||
{
|
||||
auto pool_to_use = get_pool(dev);
|
||||
auto pool_to_use { get_pool(dev) };
|
||||
|
||||
VkDescriptorSetAllocateInfo alloci {};
|
||||
alloci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
|
||||
@@ -32,6 +32,8 @@ auto GraphicsPipelineBuilder::clear() -> GraphicsPipelineBuilder &
|
||||
m_render_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
|
||||
|
||||
m_shader_stages.clear();
|
||||
m_vertex_bindings.clear();
|
||||
m_vertex_attributes.clear();
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -168,6 +170,17 @@ auto GraphicsPipelineBuilder::set_pipeline_layout(VkPipelineLayout layout)
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto GraphicsPipelineBuilder::set_vertex_input(
|
||||
std::span<VkVertexInputBindingDescription const> bindings,
|
||||
std::span<VkVertexInputAttributeDescription const> attributes)
|
||||
-> GraphicsPipelineBuilder &
|
||||
{
|
||||
m_vertex_bindings.assign(bindings.begin(), bindings.end());
|
||||
m_vertex_attributes.assign(attributes.begin(), attributes.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto GraphicsPipelineBuilder::disable_depth_testing()
|
||||
-> GraphicsPipelineBuilder &
|
||||
{
|
||||
@@ -223,6 +236,12 @@ auto GraphicsPipelineBuilder::build(VkDevice dev) -> VkPipeline
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_ci {};
|
||||
vertex_input_ci.sType
|
||||
= VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertex_input_ci.vertexBindingDescriptionCount
|
||||
= static_cast<uint32_t>(m_vertex_bindings.size());
|
||||
vertex_input_ci.pVertexBindingDescriptions = m_vertex_bindings.data();
|
||||
vertex_input_ci.vertexAttributeDescriptionCount
|
||||
= static_cast<uint32_t>(m_vertex_attributes.size());
|
||||
vertex_input_ci.pVertexAttributeDescriptions = m_vertex_attributes.data();
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_ci {};
|
||||
pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
@@ -35,6 +36,10 @@ struct GraphicsPipelineBuilder {
|
||||
auto set_depth_format(VkFormat format) -> GraphicsPipelineBuilder &;
|
||||
auto set_pipeline_layout(VkPipelineLayout layout)
|
||||
-> GraphicsPipelineBuilder &;
|
||||
auto set_vertex_input(
|
||||
std::span<VkVertexInputBindingDescription const> bindings,
|
||||
std::span<VkVertexInputAttributeDescription const> attributes)
|
||||
-> GraphicsPipelineBuilder &;
|
||||
auto disable_depth_testing() -> GraphicsPipelineBuilder &;
|
||||
auto enable_depth_testing(bool depth_write_enable = true,
|
||||
VkCompareOp op = VK_COMPARE_OP_LESS_OR_EQUAL)
|
||||
@@ -52,6 +57,8 @@ private:
|
||||
VkFormat m_color_attachment_format {};
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> m_shader_stages {};
|
||||
std::vector<VkVertexInputBindingDescription> m_vertex_bindings {};
|
||||
std::vector<VkVertexInputAttributeDescription> m_vertex_attributes {};
|
||||
|
||||
Logger &m_logger;
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ auto Mesh::load_gltf_meshes(
|
||||
{
|
||||
renderer.logger().debug("Loading GLTF from file: {}", path);
|
||||
|
||||
auto data = fastgltf::GltfDataBuffer::FromPath(path);
|
||||
auto data { fastgltf::GltfDataBuffer::FromPath(path) };
|
||||
if (data.error() != fastgltf::Error::None) {
|
||||
renderer.logger().err("Failed to open glTF file: {} (error {})", path,
|
||||
fastgltf::to_underlying(data.error()));
|
||||
@@ -98,7 +98,7 @@ auto Mesh::load_gltf_meshes(
|
||||
new_surface.count = static_cast<uint32_t>(
|
||||
gltf.accessors[p.indicesAccessor.value()].count);
|
||||
|
||||
size_t initial_vertex = vertices.size();
|
||||
size_t initial_vertex { vertices.size() };
|
||||
|
||||
{ // Indices
|
||||
auto &accessor = gltf.accessors[p.indicesAccessor.value()];
|
||||
@@ -128,7 +128,7 @@ auto Mesh::load_gltf_meshes(
|
||||
|
||||
if (auto attr = p.findAttribute("NORMAL")) { // Normals
|
||||
auto &accessor = gltf.accessors[attr->accessorIndex];
|
||||
size_t local_index = 0;
|
||||
size_t local_index { 0 };
|
||||
for (auto normal :
|
||||
fastgltf::iterateAccessor<smath::Vec3>(gltf, accessor)) {
|
||||
vertices[initial_vertex + local_index].normal = normal;
|
||||
@@ -138,7 +138,7 @@ auto Mesh::load_gltf_meshes(
|
||||
|
||||
if (auto attr = p.findAttribute("TEXCOORD_0")) { // UVs
|
||||
auto &accessor = gltf.accessors[attr->accessorIndex];
|
||||
size_t local_index = 0;
|
||||
size_t local_index { 0 };
|
||||
for (auto uv :
|
||||
fastgltf::iterateAccessor<smath::Vec2>(gltf, accessor)) {
|
||||
uv.unpack(vertices[initial_vertex + local_index].u,
|
||||
@@ -149,7 +149,7 @@ auto Mesh::load_gltf_meshes(
|
||||
|
||||
if (auto attr = p.findAttribute("COLOR_0")) { // Colors
|
||||
auto &accessor = gltf.accessors[attr->accessorIndex];
|
||||
size_t local_index = 0;
|
||||
size_t local_index { 0 };
|
||||
|
||||
switch (accessor.type) {
|
||||
case fastgltf::AccessorType::Vec3: {
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
static std::filesystem::path get_log_path(std::string_view app_name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
PWSTR path = nullptr;
|
||||
PWSTR path { nullptr };
|
||||
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path);
|
||||
std::wstring wpath(path);
|
||||
CoTaskMemFree(path);
|
||||
@@ -70,7 +70,7 @@ static int compress_file(std::filesystem::path const &input_path,
|
||||
std::vector<char> buffer(chunk_size);
|
||||
while (in) {
|
||||
in.read(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||
std::streamsize bytes = in.gcount();
|
||||
std::streamsize bytes { in.gcount() };
|
||||
if (bytes > 0)
|
||||
gzwrite(out, buffer.data(), static_cast<unsigned int>(bytes));
|
||||
}
|
||||
@@ -99,20 +99,20 @@ Logger::Logger(std::string_view app_name)
|
||||
if (!file.is_regular_file())
|
||||
continue;
|
||||
|
||||
auto name = file.path().filename().stem().string();
|
||||
auto name { file.path().filename().stem().string() };
|
||||
constexpr std::string_view prefix = "log_";
|
||||
|
||||
if (name.rfind(prefix, 0) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int v = std::stoi(name.substr(prefix.size()));
|
||||
int v { std::stoi(name.substr(prefix.size())) };
|
||||
if (v > max)
|
||||
max = v;
|
||||
|
||||
auto ext = file.path().filename().extension().string();
|
||||
auto ext { file.path().filename().extension().string() };
|
||||
if (ext == ".txt") {
|
||||
auto np = file.path();
|
||||
auto np { file.path() };
|
||||
np.replace_extension(ext + ".gz");
|
||||
compress_file(file.path(), np);
|
||||
}
|
||||
@@ -153,7 +153,7 @@ static std::string get_current_time_string()
|
||||
|
||||
void Logger::log(Level level, std::string_view msg)
|
||||
{
|
||||
auto time_str = get_current_time_string();
|
||||
auto time_str { get_current_time_string() };
|
||||
std::string level_str;
|
||||
switch (level) {
|
||||
case Logger::Level::Debug:
|
||||
|
||||
@@ -27,13 +27,13 @@ auto Pipeline::Builder::set_push_constant_ranges(
|
||||
auto Pipeline::Builder::build_compute(
|
||||
vk::PipelineShaderStageCreateInfo const &stage) -> Pipeline
|
||||
{
|
||||
auto pipeline_layout = build_layout();
|
||||
auto pipeline_layout { build_layout() };
|
||||
|
||||
vk::ComputePipelineCreateInfo pipeline_ci {};
|
||||
pipeline_ci.layout = pipeline_layout.get();
|
||||
pipeline_ci.stage = stage;
|
||||
|
||||
auto pipeline_ret = m_device.createComputePipelineUnique({}, pipeline_ci);
|
||||
auto pipeline_ret { m_device.createComputePipelineUnique({}, pipeline_ci) };
|
||||
VK_CHECK(m_logger, pipeline_ret.result);
|
||||
|
||||
return Pipeline {
|
||||
@@ -46,14 +46,14 @@ auto Pipeline::Builder::build_graphics(
|
||||
std::function<GraphicsPipelineBuilder &(GraphicsPipelineBuilder &)> const
|
||||
&configure) -> Pipeline
|
||||
{
|
||||
auto pipeline_layout = build_layout();
|
||||
auto pipeline_layout { build_layout() };
|
||||
|
||||
auto builder = GraphicsPipelineBuilder { m_logger };
|
||||
auto builder { GraphicsPipelineBuilder { m_logger } };
|
||||
builder.set_pipeline_layout(
|
||||
static_cast<VkPipelineLayout>(pipeline_layout.get()));
|
||||
configure(builder);
|
||||
|
||||
auto pipeline_handle = builder.build(static_cast<VkDevice>(m_device));
|
||||
auto pipeline_handle { builder.build(static_cast<VkDevice>(m_device)) };
|
||||
vk::UniquePipeline pipeline_unique(pipeline_handle,
|
||||
vk::detail::ObjectDestroy<vk::Device,
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER_TYPE>(m_device));
|
||||
|
||||
314
src/Skybox.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
#include "Skybox.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "CPUTexture.h"
|
||||
#include "DescriptorWriter.h"
|
||||
#include "GraphicsPipelineBuilder.h"
|
||||
#include "Util.h"
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
namespace {
|
||||
|
||||
struct SkyboxPushConstants {
|
||||
smath::Mat4 mvp;
|
||||
};
|
||||
|
||||
struct FaceOffset {
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
};
|
||||
|
||||
constexpr std::array<FaceOffset, 6> CROSS_OFFSETS {
|
||||
FaceOffset { 2, 1 }, // +X
|
||||
FaceOffset { 0, 1 }, // -X
|
||||
FaceOffset { 1, 0 }, // +Y
|
||||
FaceOffset { 1, 2 }, // -Y
|
||||
FaceOffset { 1, 1 }, // +Z
|
||||
FaceOffset { 3, 1 }, // -Z
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
auto Skybox::rebuild_pipeline(VulkanRenderer &renderer) -> bool
|
||||
{
|
||||
Pipeline::Builder pipeline_builder { renderer.device(), renderer.logger() };
|
||||
|
||||
uint8_t skybox_vert_shader_data[] {
|
||||
#embed "skybox_vert.spv"
|
||||
};
|
||||
auto skybox_vert_shader
|
||||
= vkutil::load_shader_module(std::span<uint8_t>(skybox_vert_shader_data,
|
||||
sizeof(skybox_vert_shader_data)),
|
||||
renderer.device());
|
||||
if (!skybox_vert_shader) {
|
||||
renderer.logger().err("Failed to load skybox vert shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t skybox_frag_shader_data[] {
|
||||
#embed "skybox_frag.spv"
|
||||
};
|
||||
auto skybox_frag_shader
|
||||
= vkutil::load_shader_module(std::span<uint8_t>(skybox_frag_shader_data,
|
||||
sizeof(skybox_frag_shader_data)),
|
||||
renderer.device());
|
||||
if (!skybox_frag_shader) {
|
||||
renderer.logger().err("Failed to load skybox frag shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
vk::PushConstantRange push_constant_range {};
|
||||
push_constant_range.stageFlags = vk::ShaderStageFlagBits::eVertex;
|
||||
push_constant_range.offset = 0;
|
||||
push_constant_range.size = sizeof(SkyboxPushConstants);
|
||||
|
||||
std::array push_constant_ranges { push_constant_range };
|
||||
pipeline_builder.set_push_constant_ranges(push_constant_ranges);
|
||||
std::array descriptor_set_layouts {
|
||||
renderer.single_image_descriptor_layout()
|
||||
};
|
||||
pipeline_builder.set_descriptor_set_layouts(descriptor_set_layouts);
|
||||
|
||||
VkVertexInputBindingDescription binding {};
|
||||
binding.binding = 0;
|
||||
binding.stride = sizeof(Vertex);
|
||||
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
VkVertexInputAttributeDescription attribute {};
|
||||
attribute.location = 0;
|
||||
attribute.binding = 0;
|
||||
attribute.format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
attribute.offset = offsetof(Vertex, position);
|
||||
|
||||
std::array bindings { binding };
|
||||
std::array attributes { attribute };
|
||||
|
||||
m_pipeline = pipeline_builder.build_graphics(
|
||||
[&](GraphicsPipelineBuilder &builder) -> GraphicsPipelineBuilder & {
|
||||
builder.set_vertex_input(bindings, attributes);
|
||||
return builder
|
||||
.set_shaders(skybox_vert_shader.get(), skybox_frag_shader.get())
|
||||
.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
|
||||
.set_polygon_mode(VK_POLYGON_MODE_FILL)
|
||||
.set_cull_mode(
|
||||
VK_CULL_MODE_FRONT_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE)
|
||||
.set_multisampling(
|
||||
static_cast<VkSampleCountFlagBits>(renderer.msaa_samples()))
|
||||
.disable_blending()
|
||||
.enable_depth_testing(false, VK_COMPARE_OP_LESS_OR_EQUAL)
|
||||
.set_color_attachment_format(
|
||||
static_cast<VkFormat>(renderer.draw_image_format()))
|
||||
.set_depth_format(
|
||||
static_cast<VkFormat>(renderer.depth_image_format()));
|
||||
});
|
||||
|
||||
m_pipeline_samples = renderer.msaa_samples();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Skybox::init(VulkanRenderer &renderer, std::filesystem::path const &path)
|
||||
-> void
|
||||
{
|
||||
if (ok) {
|
||||
destroy(renderer);
|
||||
ok = false;
|
||||
}
|
||||
|
||||
CPUTexture texture { path };
|
||||
|
||||
if (texture.width == 0 || texture.height == 0) {
|
||||
renderer.logger().err("Skybox texture is empty: {}", path.string());
|
||||
ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture.width % 4 != 0 || texture.height % 3 != 0
|
||||
|| texture.width / 4 != texture.height / 3) {
|
||||
renderer.logger().err(
|
||||
"Skybox texture must be 4x3 faces: {}", path.string());
|
||||
ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t const face_size = texture.width / 4;
|
||||
size_t const face_bytes = static_cast<size_t>(face_size) * face_size * 4;
|
||||
|
||||
std::vector<uint8_t> cubemap_pixels(face_bytes * CROSS_OFFSETS.size());
|
||||
|
||||
for (size_t face = 0; face < CROSS_OFFSETS.size(); ++face) {
|
||||
auto const offset = CROSS_OFFSETS[face];
|
||||
for (uint32_t y = 0; y < face_size; ++y) {
|
||||
for (uint32_t x = 0; x < face_size; ++x) {
|
||||
uint32_t const src_x = offset.x * face_size + x;
|
||||
uint32_t const src_y = offset.y * face_size + y;
|
||||
size_t const src_index
|
||||
= (static_cast<size_t>(src_y) * texture.width + src_x) * 4;
|
||||
size_t const dst_index = face * face_bytes
|
||||
+ (static_cast<size_t>(y) * face_size + x) * 4;
|
||||
std::copy_n(texture.pixels.data() + src_index, 4,
|
||||
cubemap_pixels.data() + dst_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_cubemap = renderer.create_cubemap(cubemap_pixels, face_size,
|
||||
texture.format, vk::ImageUsageFlagBits::eSampled);
|
||||
if (!m_cubemap.image) {
|
||||
renderer.logger().err("Failed to create cubemap image");
|
||||
return;
|
||||
}
|
||||
|
||||
vk::SamplerCreateInfo sampler_ci {};
|
||||
sampler_ci.magFilter = vk::Filter::eLinear;
|
||||
sampler_ci.minFilter = vk::Filter::eLinear;
|
||||
sampler_ci.mipmapMode = vk::SamplerMipmapMode::eLinear;
|
||||
sampler_ci.addressModeU = vk::SamplerAddressMode::eClampToEdge;
|
||||
sampler_ci.addressModeV = vk::SamplerAddressMode::eClampToEdge;
|
||||
sampler_ci.addressModeW = vk::SamplerAddressMode::eClampToEdge;
|
||||
m_sampler = renderer.device().createSamplerUnique(sampler_ci);
|
||||
|
||||
vk::DescriptorPoolSize pool_size {};
|
||||
pool_size.type = vk::DescriptorType::eCombinedImageSampler;
|
||||
pool_size.descriptorCount = 1;
|
||||
|
||||
vk::DescriptorPoolCreateInfo pool_ci {};
|
||||
pool_ci.maxSets = 1;
|
||||
pool_ci.poolSizeCount = 1;
|
||||
pool_ci.pPoolSizes = &pool_size;
|
||||
m_descriptor_pool = renderer.device().createDescriptorPoolUnique(pool_ci);
|
||||
|
||||
vk::DescriptorSetAllocateInfo alloc_info {};
|
||||
alloc_info.descriptorPool = m_descriptor_pool.get();
|
||||
alloc_info.descriptorSetCount = 1;
|
||||
vk::DescriptorSetLayout layout {
|
||||
renderer.single_image_descriptor_layout()
|
||||
};
|
||||
alloc_info.pSetLayouts = &layout;
|
||||
m_descriptor_set
|
||||
= renderer.device().allocateDescriptorSets(alloc_info).front();
|
||||
|
||||
DescriptorWriter()
|
||||
.write_image(0, m_cubemap.image_view, m_sampler.get(),
|
||||
static_cast<VkImageLayout>(vk::ImageLayout::eShaderReadOnlyOptimal),
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
.update_set(renderer.device(), m_descriptor_set);
|
||||
|
||||
std::vector<Vertex> vertices;
|
||||
vertices.reserve(8);
|
||||
|
||||
auto push_vertex = [&](smath::Vec3 const &pos) {
|
||||
Vertex v {};
|
||||
v.position = pos;
|
||||
v.u = 0.0f;
|
||||
v.v = 0.0f;
|
||||
v.normal = smath::Vec3 { 0.0f, 0.0f, 1.0f };
|
||||
v.color = smath::Vec4 { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
vertices.emplace_back(v);
|
||||
};
|
||||
|
||||
push_vertex(smath::Vec3 { -1.0f, -1.0f, -1.0f });
|
||||
push_vertex(smath::Vec3 { 1.0f, -1.0f, -1.0f });
|
||||
push_vertex(smath::Vec3 { 1.0f, 1.0f, -1.0f });
|
||||
push_vertex(smath::Vec3 { -1.0f, 1.0f, -1.0f });
|
||||
push_vertex(smath::Vec3 { -1.0f, -1.0f, 1.0f });
|
||||
push_vertex(smath::Vec3 { 1.0f, -1.0f, 1.0f });
|
||||
push_vertex(smath::Vec3 { 1.0f, 1.0f, 1.0f });
|
||||
push_vertex(smath::Vec3 { -1.0f, 1.0f, 1.0f });
|
||||
|
||||
std::vector<uint32_t> indices {
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
4,
|
||||
6,
|
||||
7, // +Z
|
||||
1,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
3,
|
||||
2, // -Z
|
||||
5,
|
||||
1,
|
||||
2,
|
||||
5,
|
||||
2,
|
||||
6, // +X
|
||||
0,
|
||||
4,
|
||||
7,
|
||||
0,
|
||||
7,
|
||||
3, // -X
|
||||
7,
|
||||
6,
|
||||
2,
|
||||
7,
|
||||
2,
|
||||
3, // +Y
|
||||
0,
|
||||
1,
|
||||
5,
|
||||
0,
|
||||
5,
|
||||
4, // -Y
|
||||
};
|
||||
|
||||
m_index_count = static_cast<uint32_t>(indices.size());
|
||||
m_cube_mesh = renderer.upload_mesh(indices, vertices);
|
||||
|
||||
if (!rebuild_pipeline(renderer)) {
|
||||
ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
auto Skybox::destroy(VulkanRenderer &renderer) -> void
|
||||
{
|
||||
if (m_cube_mesh.index_buffer.buffer) {
|
||||
renderer.destroy_buffer(m_cube_mesh.index_buffer);
|
||||
}
|
||||
if (m_cube_mesh.vertex_buffer.buffer) {
|
||||
renderer.destroy_buffer(m_cube_mesh.vertex_buffer);
|
||||
}
|
||||
if (m_cubemap.image) {
|
||||
renderer.destroy_image(m_cubemap);
|
||||
}
|
||||
m_sampler.reset();
|
||||
m_descriptor_pool.reset();
|
||||
m_pipeline.reset();
|
||||
m_pipeline_samples = vk::SampleCountFlagBits::e1;
|
||||
m_descriptor_set = vk::DescriptorSet {};
|
||||
m_cube_mesh = {};
|
||||
m_cubemap = {};
|
||||
m_index_count = 0;
|
||||
ok = false;
|
||||
}
|
||||
|
||||
auto Skybox::draw(VulkanRenderer::GL &gl, VulkanRenderer &renderer,
|
||||
smath::Mat4 const &mvp) -> void
|
||||
{
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pipeline_samples != renderer.msaa_samples()) {
|
||||
if (!rebuild_pipeline(renderer)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SkyboxPushConstants push_constants { mvp };
|
||||
auto bytes { std::as_bytes(std::span { &push_constants, 1 }) };
|
||||
gl.draw_indexed(m_pipeline, m_descriptor_set, m_cube_mesh.vertex_buffer,
|
||||
m_cube_mesh.index_buffer, m_index_count, bytes);
|
||||
}
|
||||
|
||||
} // namespace Lunar
|
||||
36
src/Skybox.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "Pipeline.h"
|
||||
#include "Types.h"
|
||||
#include "VulkanRenderer.h"
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
struct Skybox {
|
||||
bool ok { false };
|
||||
|
||||
auto init(VulkanRenderer &renderer, std::filesystem::path const &path)
|
||||
-> void;
|
||||
auto destroy(VulkanRenderer &renderer) -> void;
|
||||
auto draw(VulkanRenderer::GL &gl, VulkanRenderer &renderer,
|
||||
smath::Mat4 const &mvp) -> void;
|
||||
|
||||
private:
|
||||
auto rebuild_pipeline(VulkanRenderer &renderer) -> bool;
|
||||
|
||||
Pipeline m_pipeline {};
|
||||
GPUMeshBuffers m_cube_mesh {};
|
||||
AllocatedImage m_cubemap {};
|
||||
vk::UniqueSampler m_sampler {};
|
||||
vk::UniqueDescriptorPool m_descriptor_pool {};
|
||||
vk::DescriptorSet m_descriptor_set {};
|
||||
vk::SampleCountFlagBits m_pipeline_samples { vk::SampleCountFlagBits::e1 };
|
||||
uint32_t m_index_count { 0 };
|
||||
};
|
||||
|
||||
} // namespace Lunar
|
||||
13
src/Types.h
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <smath.hpp>
|
||||
#include <vk_mem_alloc.h>
|
||||
@@ -33,6 +35,15 @@ struct FrameData {
|
||||
|
||||
DeletionQueue deletion_queue;
|
||||
DescriptorAllocatorGrowable frame_descriptors;
|
||||
AllocatedBuffer frame_image_buffer {};
|
||||
vk::Extent2D frame_image_extent {};
|
||||
std::vector<std::uint8_t> frame_image_rgba;
|
||||
bool frame_image_ready { false };
|
||||
bool tracy_frame_ready { false };
|
||||
AllocatedBuffer screenshot_buffer {};
|
||||
vk::Extent2D screenshot_extent {};
|
||||
std::vector<std::uint8_t> screenshot_rgba;
|
||||
bool screenshot_ready { false };
|
||||
};
|
||||
|
||||
struct Vertex {
|
||||
@@ -85,7 +96,7 @@ struct PolarCoordinate {
|
||||
|
||||
smath::Vec3 to_vec3() const
|
||||
{
|
||||
float sin_phi = std::sin(phi);
|
||||
float sin_phi { std::sin(phi) };
|
||||
|
||||
return smath::Vec3 { r * sin_phi * std::cos(theta), r * std::cos(phi),
|
||||
r * sin_phi * std::sin(theta) };
|
||||
|
||||
10
src/Util.h
@@ -29,13 +29,21 @@ template<typename F> privDefer<F> defer_func(F f) { return privDefer<F>(f); }
|
||||
#define VK_CHECK(logger, x) \
|
||||
do { \
|
||||
auto err { x }; \
|
||||
auto result = vk::Result(err); \
|
||||
auto result { vk::Result(err) }; \
|
||||
if (result != vk::Result::eSuccess) { \
|
||||
(logger).err("Detected Vulkan error: {}", vk::to_string(result)); \
|
||||
throw std::runtime_error("Vulkan error"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#if defined(TRACY_ENABLE)
|
||||
# define GZoneScopedN(name) ZoneScopedN(name)
|
||||
#else
|
||||
# define GZoneScopedN(name) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
namespace vkutil {
|
||||
|
||||
auto transition_image(vk::CommandBuffer cmd, vk::Image image,
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
@@ -13,6 +17,7 @@
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "CPUTexture.h"
|
||||
#include "Colors.h"
|
||||
#include "DeletionQueue.h"
|
||||
#include "Loader.h"
|
||||
@@ -30,14 +35,13 @@ struct GPUDrawPushConstants {
|
||||
constexpr unsigned FRAME_OVERLAP = 2;
|
||||
|
||||
struct VulkanRenderer {
|
||||
enum class AntiAliasingKind {
|
||||
NONE,
|
||||
MSAA_2X,
|
||||
MSAA_4X,
|
||||
MSAA_8X,
|
||||
struct ScreenshotPixels {
|
||||
std::span<std::uint8_t const> pixels;
|
||||
vk::Extent2D extent;
|
||||
};
|
||||
|
||||
struct GL {
|
||||
|
||||
enum class GeometryKind {
|
||||
Triangles,
|
||||
TriangleStrip,
|
||||
@@ -90,6 +94,10 @@ struct VulkanRenderer {
|
||||
auto draw_mesh(GPUMeshBuffers const &mesh, smath::Mat4 const &transform,
|
||||
uint32_t index_count, uint32_t first_index = 0,
|
||||
int32_t vertex_offset = 0) -> void;
|
||||
auto draw_indexed(Pipeline &pipeline, vk::DescriptorSet descriptor_set,
|
||||
AllocatedBuffer const &vertex_buffer,
|
||||
AllocatedBuffer const &index_buffer, uint32_t index_count,
|
||||
std::span<std::byte const> push_constants) -> void;
|
||||
|
||||
private:
|
||||
auto push_vertex(smath::Vec3 const &pos) -> void;
|
||||
@@ -116,12 +124,30 @@ struct VulkanRenderer {
|
||||
std::vector<uint32_t> m_indices;
|
||||
};
|
||||
|
||||
VulkanRenderer(SDL_Window *window, Logger &logger);
|
||||
enum class AntiAliasingKind {
|
||||
NONE,
|
||||
MSAA_2X,
|
||||
MSAA_4X,
|
||||
MSAA_8X,
|
||||
};
|
||||
|
||||
struct KmsSurfaceConfig { };
|
||||
|
||||
VulkanRenderer(SDL_Window *window, Logger &logger,
|
||||
std::span<std::string const> instance_extensions = {},
|
||||
std::span<std::string const> device_extensions = {});
|
||||
VulkanRenderer(KmsSurfaceConfig config, Logger &logger,
|
||||
std::span<std::string const> instance_extensions = {},
|
||||
std::span<std::string const> device_extensions = {});
|
||||
~VulkanRenderer();
|
||||
|
||||
auto render(std::function<void(GL &)> const &record = {}) -> void;
|
||||
auto render_to_image(vk::Image target_image, vk::Extent2D target_extent,
|
||||
std::function<void(GL &)> const &record = {}) -> void;
|
||||
auto resize(uint32_t width, uint32_t height) -> void;
|
||||
auto set_offscreen_extent(vk::Extent2D extent) -> void;
|
||||
auto set_antialiasing(AntiAliasingKind kind) -> void;
|
||||
auto set_antialiasing_immediate(AntiAliasingKind kind) -> void;
|
||||
auto antialiasing() const -> AntiAliasingKind
|
||||
{
|
||||
return m_vk.antialiasing_kind;
|
||||
@@ -132,14 +158,17 @@ struct VulkanRenderer {
|
||||
bool clear_frame_descriptors = true) -> void;
|
||||
auto upload_mesh(std::span<uint32_t> indices, std::span<Vertex> vertices)
|
||||
-> GPUMeshBuffers;
|
||||
auto destroy_buffer(AllocatedBuffer const &buffer) -> void;
|
||||
auto create_image(CPUTexture const &texture, vk::ImageUsageFlags flags,
|
||||
bool mipmapped = false) -> AllocatedImage;
|
||||
auto create_cubemap(std::span<uint8_t const> pixels, uint32_t face_size,
|
||||
vk::Format format, vk::ImageUsageFlags flags) -> AllocatedImage;
|
||||
auto destroy_image(AllocatedImage const &img) -> void;
|
||||
auto destroy_image_later(AllocatedImage img) -> void;
|
||||
auto rectangle_mesh() const -> GPUMeshBuffers const &
|
||||
{
|
||||
return m_vk.rectangle;
|
||||
}
|
||||
auto test_meshes() const -> std::vector<std::shared_ptr<Mesh>> const &
|
||||
{
|
||||
return m_vk.test_meshes;
|
||||
}
|
||||
auto white_texture() const -> AllocatedImage const &
|
||||
{
|
||||
return m_vk.white_image;
|
||||
@@ -158,13 +187,63 @@ struct VulkanRenderer {
|
||||
}
|
||||
auto draw_extent() const -> vk::Extent2D { return m_vk.draw_extent; }
|
||||
auto mesh_pipeline() -> Pipeline & { return m_vk.mesh_pipeline; }
|
||||
auto wayland_pipeline() -> Pipeline & { return m_vk.wayland_pipeline; }
|
||||
auto triangle_pipeline() -> Pipeline & { return m_vk.triangle_pipeline; }
|
||||
auto device() const -> vk::Device { return m_device; }
|
||||
auto instance() const -> vk::Instance { return m_instance; }
|
||||
auto physical_device() const -> vk::PhysicalDevice
|
||||
{
|
||||
return m_physical_device;
|
||||
}
|
||||
auto graphics_queue() const -> vk::Queue { return m_vk.graphics_queue; }
|
||||
auto graphics_queue_family() const -> uint32_t
|
||||
{
|
||||
return m_vk.graphics_queue_family;
|
||||
}
|
||||
auto draw_image_format() const -> vk::Format
|
||||
{
|
||||
return m_vk.draw_image.format;
|
||||
}
|
||||
auto depth_image_format() const -> vk::Format
|
||||
{
|
||||
return m_vk.depth_image.format;
|
||||
}
|
||||
auto msaa_samples() const -> vk::SampleCountFlagBits
|
||||
{
|
||||
return m_vk.msaa_samples;
|
||||
}
|
||||
auto single_image_descriptor_layout() const -> vk::DescriptorSetLayout
|
||||
{
|
||||
return m_vk.single_image_descriptor_layout;
|
||||
}
|
||||
auto gl_api() -> GL & { return gl; }
|
||||
auto get_screenshot() const -> std::optional<AllocatedImage>
|
||||
{
|
||||
return m_latest_screenshot;
|
||||
}
|
||||
auto get_screenshot_pixels() const
|
||||
-> std::optional<VulkanRenderer::ScreenshotPixels>
|
||||
{
|
||||
if (m_latest_screenshot_pixels.empty()
|
||||
|| m_latest_screenshot_extent.width == 0
|
||||
|| m_latest_screenshot_extent.height == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const span { std::span<std::uint8_t const> {
|
||||
m_latest_screenshot_pixels.data(),
|
||||
m_latest_screenshot_pixels.size() } };
|
||||
return ScreenshotPixels { span, m_latest_screenshot_extent };
|
||||
}
|
||||
|
||||
auto logger() const -> Logger & { return m_logger; }
|
||||
|
||||
GL gl;
|
||||
|
||||
std::optional<AllocatedImage> m_latest_screenshot {};
|
||||
std::vector<std::uint8_t> m_latest_screenshot_pixels {};
|
||||
vk::Extent2D m_latest_screenshot_extent {};
|
||||
|
||||
private:
|
||||
struct RenderCommand {
|
||||
struct SetAntiAliasing {
|
||||
@@ -176,6 +255,7 @@ private:
|
||||
|
||||
auto vk_init() -> void;
|
||||
auto swapchain_init() -> void;
|
||||
auto setup_kms_surface() -> void;
|
||||
auto commands_init() -> void;
|
||||
auto sync_init() -> void;
|
||||
auto descriptors_init() -> void;
|
||||
@@ -197,17 +277,27 @@ private:
|
||||
auto destroy_msaa_color_image() -> void;
|
||||
auto recreate_swapchain(uint32_t width, uint32_t height) -> void;
|
||||
auto destroy_swapchain() -> void;
|
||||
auto ensure_screenshot_buffers(vk::Extent2D extent) -> void;
|
||||
auto destroy_screenshot_buffers() -> void;
|
||||
auto emit_frame_screenshot(FrameData &frame) -> void;
|
||||
#if defined(TRACY_ENABLE)
|
||||
auto ensure_tracy_frame_buffers(vk::Extent2D extent) -> void;
|
||||
auto destroy_tracy_frame_buffers() -> void;
|
||||
auto emit_tracy_frame_image(FrameData &frame) -> void;
|
||||
#endif
|
||||
auto create_image(vk::Extent3D size, vk::Format format,
|
||||
vk::ImageUsageFlags flags,
|
||||
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1,
|
||||
bool mipmapped = false) -> AllocatedImage;
|
||||
auto create_image_no_view(vk::Extent3D size, vk::Format format,
|
||||
vk::ImageUsageFlags flags,
|
||||
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1,
|
||||
bool mipmapped = false) -> AllocatedImage;
|
||||
auto create_image(void const *data, vk::Extent3D size, vk::Format format,
|
||||
vk::ImageUsageFlags flags, bool mipmapped = false) -> AllocatedImage;
|
||||
auto destroy_image(AllocatedImage const &img) -> void;
|
||||
|
||||
auto create_buffer(size_t alloc_size, vk::BufferUsageFlags usage,
|
||||
VmaMemoryUsage memory_usage) -> AllocatedBuffer;
|
||||
auto destroy_buffer(AllocatedBuffer const &buffer) -> void;
|
||||
auto enqueue_render_command(RenderCommand &&command) -> void;
|
||||
auto process_render_commands() -> void;
|
||||
auto apply_antialiasing(AntiAliasingKind kind) -> void;
|
||||
@@ -248,6 +338,13 @@ private:
|
||||
vk::ImageLayout msaa_color_image_layout { vk::ImageLayout::eUndefined };
|
||||
AllocatedImage depth_image {};
|
||||
vk::ImageLayout depth_image_layout { vk::ImageLayout::eUndefined };
|
||||
#if defined(TRACY_ENABLE)
|
||||
AllocatedImage tracy_capture_image {};
|
||||
vk::ImageLayout tracy_capture_image_layout {
|
||||
vk::ImageLayout::eUndefined
|
||||
};
|
||||
vk::Extent2D tracy_capture_extent {};
|
||||
#endif
|
||||
vk::Extent2D draw_extent {};
|
||||
AntiAliasingKind antialiasing_kind { AntiAliasingKind::NONE };
|
||||
vk::SampleCountFlagBits msaa_samples { vk::SampleCountFlagBits::e1 };
|
||||
@@ -264,6 +361,7 @@ private:
|
||||
Pipeline triangle_pipeline_culled;
|
||||
Pipeline mesh_pipeline;
|
||||
Pipeline mesh_pipeline_culled;
|
||||
Pipeline wayland_pipeline;
|
||||
|
||||
GPUMeshBuffers rectangle;
|
||||
|
||||
@@ -277,8 +375,6 @@ private:
|
||||
|
||||
uint64_t frame_number { 0 };
|
||||
|
||||
std::vector<std::shared_ptr<Mesh>> test_meshes;
|
||||
|
||||
AllocatedImage white_image {};
|
||||
AllocatedImage black_image {};
|
||||
AllocatedImage gray_image {};
|
||||
@@ -288,10 +384,27 @@ private:
|
||||
vk::UniqueSampler default_sampler_nearest;
|
||||
} m_vk;
|
||||
|
||||
struct KmsState {
|
||||
vk::DisplayKHR display {};
|
||||
vk::DisplayModeKHR mode {};
|
||||
vk::Extent2D extent {};
|
||||
uint32_t plane_index { 0 };
|
||||
uint32_t plane_stack_index { 0 };
|
||||
std::string display_name {};
|
||||
};
|
||||
|
||||
SDL_Window *m_window { nullptr };
|
||||
Logger &m_logger;
|
||||
std::mutex m_command_mutex;
|
||||
std::vector<RenderCommand> m_pending_render_commands;
|
||||
bool m_use_kms { false };
|
||||
bool m_imgui_enabled { true };
|
||||
std::optional<KmsState> m_kms_state {};
|
||||
vk::PhysicalDevice m_kms_physical_device {};
|
||||
vk::Extent2D m_kms_extent {};
|
||||
bool m_kms_physical_device_set { false };
|
||||
std::vector<std::string> m_extra_instance_extensions {};
|
||||
std::vector<std::string> m_extra_device_extensions {};
|
||||
};
|
||||
|
||||
} // namespace Lunar
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
#include "src/Application.h"
|
||||
|
||||
auto main() -> int
|
||||
{
|
||||
Lunar::Application app {};
|
||||
app.run();
|
||||
}
|
||||
auto main() -> int { Lunar::Application::the().run(); }
|
||||
|
||||
113
src/wayland/Client.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "Display.h"
|
||||
#include "List.h"
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
struct Client {
|
||||
Client(wl_client *client)
|
||||
: m_client { std::move(client) }
|
||||
{
|
||||
assert(m_client);
|
||||
}
|
||||
~Client() = default;
|
||||
|
||||
inline auto c_ptr() const -> wl_client * { return m_client; }
|
||||
|
||||
static auto from_link(wl_list *link) -> Client
|
||||
{
|
||||
return Client { wl_client_from_link(link) };
|
||||
}
|
||||
|
||||
auto flush() { wl_client_flush(m_client); }
|
||||
|
||||
auto get_display() -> Display
|
||||
{
|
||||
return Display { wl_client_get_display(m_client) };
|
||||
}
|
||||
|
||||
auto get_credentials() noexcept -> std::tuple<pid_t, uid_t, gid_t>
|
||||
{
|
||||
std::tuple<pid_t, uid_t, gid_t> ret {};
|
||||
auto &[pid, uid, gid] { ret };
|
||||
wl_client_get_credentials(m_client, &pid, &uid, &gid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto get_fd() noexcept -> int { return wl_client_get_fd(m_client); }
|
||||
|
||||
auto get_object(uint32_t id) -> std::optional<wl_resource *>
|
||||
{
|
||||
if (auto *res { wl_client_get_object(m_client, id) }; res != NULL) {
|
||||
return res;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
auto post_implementation_error(std::string_view string)
|
||||
{
|
||||
wl_client_post_implementation_error(
|
||||
m_client, "%.*s", static_cast<int>(string.size()), string.data());
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
auto post_implementation_error(
|
||||
std::format_string<Args...> fmt, Args &&...args)
|
||||
{
|
||||
post_implementation_error(
|
||||
std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
auto add_destroy_listener(wl_listener *listener)
|
||||
{
|
||||
wl_client_add_destroy_listener(m_client, listener);
|
||||
}
|
||||
|
||||
auto add_destroy_late_listener(wl_listener *listener)
|
||||
{
|
||||
wl_client_add_destroy_late_listener(m_client, listener);
|
||||
}
|
||||
|
||||
auto get_link() -> wl_list * { return wl_client_get_link(m_client); }
|
||||
|
||||
auto add_resource_created_listener(wl_listener *listener)
|
||||
{
|
||||
wl_client_add_resource_created_listener(m_client, listener);
|
||||
}
|
||||
|
||||
auto for_each_resource(
|
||||
std::function<wl_iterator_result(wl_resource *)> const &fn) -> void
|
||||
{
|
||||
wl_client_for_each_resource(
|
||||
m_client,
|
||||
(wl_client_for_each_resource_iterator_func_t)[](
|
||||
wl_resource * res, void *user_data)
|
||||
->wl_iterator_result {
|
||||
auto *f { static_cast<
|
||||
std::function<wl_iterator_result(wl_resource *)> *>(
|
||||
user_data) };
|
||||
return (*f)(res);
|
||||
},
|
||||
const_cast<void *>(static_cast<void const *>(&fn)));
|
||||
}
|
||||
|
||||
auto set_max_buffer_size(size_t max_buffer_size)
|
||||
{
|
||||
wl_client_set_max_buffer_size(m_client, max_buffer_size);
|
||||
}
|
||||
|
||||
private:
|
||||
wl_client *m_client {};
|
||||
};
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
103
src/wayland/Display.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
struct Display {
|
||||
Display()
|
||||
: m_display(wl_display_create())
|
||||
{
|
||||
}
|
||||
Display(wl_display *display)
|
||||
: m_display { std::move(display) }
|
||||
, m_should_cleanup { false }
|
||||
{
|
||||
}
|
||||
~Display()
|
||||
{
|
||||
if (!m_should_cleanup)
|
||||
return;
|
||||
wl_display_destroy_clients(m_display);
|
||||
wl_display_destroy(m_display);
|
||||
}
|
||||
|
||||
inline auto c_ptr() const -> wl_display * { return m_display; }
|
||||
|
||||
auto set_global_filter(
|
||||
wl_display_global_filter_func_t filter, void *data) noexcept
|
||||
{
|
||||
wl_display_set_global_filter(m_display, filter, data);
|
||||
}
|
||||
|
||||
auto next_serial() noexcept -> uint32_t
|
||||
{
|
||||
return wl_display_next_serial(m_display);
|
||||
}
|
||||
|
||||
auto set_default_max_buffer_size(size_t max_buffer_size) noexcept
|
||||
{
|
||||
wl_display_set_default_max_buffer_size(m_display, max_buffer_size);
|
||||
}
|
||||
|
||||
auto add_socket_fd(int fd)
|
||||
{
|
||||
if (wl_display_add_socket_fd(m_display, fd) == -1) {
|
||||
throw std::runtime_error(
|
||||
"Failed to add socket fd to Wayland display");
|
||||
}
|
||||
}
|
||||
|
||||
auto add_socket(char const *name)
|
||||
{
|
||||
if (wl_display_add_socket(m_display, name) == -1) {
|
||||
throw std::runtime_error(std::format(
|
||||
"Failed to add socket `{}` to Wayland display", name));
|
||||
}
|
||||
}
|
||||
|
||||
auto add_protocol_logger(wl_protocol_logger_func_t func, void *user_data)
|
||||
-> wl_protocol_logger *
|
||||
{
|
||||
if (auto *logger {
|
||||
wl_display_add_protocol_logger(m_display, func, user_data) };
|
||||
logger != NULL) {
|
||||
return logger;
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
"Failed to add protocol logger to Wayland display");
|
||||
}
|
||||
}
|
||||
|
||||
auto add_shm_format(uint32_t format) -> uint32_t *
|
||||
{
|
||||
if (auto *fmt { wl_display_add_shm_format(m_display, format) };
|
||||
fmt != NULL) {
|
||||
return fmt;
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
"Failed to add SHM format to Wayland display");
|
||||
}
|
||||
}
|
||||
|
||||
auto get_client_list() -> std::vector<wl_client *>
|
||||
{
|
||||
std::vector<wl_client *> ret {};
|
||||
auto const list { wl_display_get_client_list(m_display) };
|
||||
assert(list);
|
||||
wl_client *client {};
|
||||
wl_client_for_each(client, list) { ret.push_back(client); }
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
wl_display *m_display {};
|
||||
bool m_should_cleanup { true };
|
||||
};
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
71
src/wayland/Global.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "Client.h"
|
||||
#include "Display.h"
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
struct Global {
|
||||
Global() = delete;
|
||||
|
||||
explicit Global(Display &display, wl_interface const *interface,
|
||||
int version, void *data, wl_global_bind_func_t bind)
|
||||
: m_global {
|
||||
wl_global_create(display.c_ptr(), interface, version, data, bind),
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
Global(wl_global *global)
|
||||
: m_global { std::move(global) }
|
||||
, m_should_cleanup { false }
|
||||
{
|
||||
assert(m_global);
|
||||
}
|
||||
|
||||
~Global()
|
||||
{
|
||||
if (!m_should_cleanup)
|
||||
return;
|
||||
wl_global_destroy(m_global);
|
||||
}
|
||||
|
||||
inline auto c_ptr() const -> wl_global * { return m_global; }
|
||||
|
||||
auto get_name(Client &client) const -> std::optional<uint32_t>
|
||||
{
|
||||
if (auto const ret { wl_global_get_name(m_global, client.c_ptr()) };
|
||||
ret != 0) {
|
||||
return ret;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
inline auto get_version() const -> uint32_t
|
||||
{
|
||||
return wl_global_get_version(m_global);
|
||||
}
|
||||
|
||||
inline auto get_display() const -> Display
|
||||
{
|
||||
return wl_global_get_display(m_global);
|
||||
}
|
||||
|
||||
inline auto get_interface() const -> wl_interface const *
|
||||
{
|
||||
return wl_global_get_interface(m_global);
|
||||
}
|
||||
|
||||
private:
|
||||
wl_global *m_global {};
|
||||
bool m_should_cleanup { true };
|
||||
};
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
287
src/wayland/List.h
Normal file
@@ -0,0 +1,287 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T, wl_list T::*Member>
|
||||
constexpr std::ptrdiff_t member_offset() noexcept
|
||||
{
|
||||
return reinterpret_cast<std::ptrdiff_t>(
|
||||
&(reinterpret_cast<T const volatile *>(0)->*Member));
|
||||
}
|
||||
|
||||
template<typename T, wl_list T::*Member>
|
||||
inline T *container_of(wl_list *node) noexcept
|
||||
{
|
||||
auto *p { reinterpret_cast<std::byte *>(node)
|
||||
- member_offset<T, Member>() };
|
||||
return reinterpret_cast<T *>(p);
|
||||
}
|
||||
|
||||
template<typename T, wl_list T::*Member>
|
||||
inline T const *container_of(wl_list const *node) noexcept
|
||||
{
|
||||
auto *p { reinterpret_cast<std::byte const *>(node)
|
||||
- member_offset<T, Member>() };
|
||||
return reinterpret_cast<T const *>(p);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, wl_list T::*Member> struct List {
|
||||
struct Iterator {
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T *;
|
||||
using reference = T &;
|
||||
|
||||
Iterator() = default;
|
||||
Iterator(wl_list *cur, wl_list *head)
|
||||
: m_cur(cur)
|
||||
, m_head(head)
|
||||
{
|
||||
}
|
||||
|
||||
auto operator*() const -> reference
|
||||
{
|
||||
return *detail::container_of<T, Member>(m_cur);
|
||||
}
|
||||
auto operator->() const -> pointer
|
||||
{
|
||||
return detail::container_of<T, Member>(m_cur);
|
||||
}
|
||||
|
||||
auto operator++() -> Iterator &
|
||||
{
|
||||
m_cur = m_cur->next;
|
||||
return *this;
|
||||
}
|
||||
auto operator++(int) -> Iterator
|
||||
{
|
||||
auto t { *this };
|
||||
++(*this);
|
||||
return t;
|
||||
}
|
||||
|
||||
auto operator--() -> Iterator &
|
||||
{
|
||||
m_cur = (m_cur == m_head) ? m_head->prev : m_cur->prev;
|
||||
return *this;
|
||||
}
|
||||
auto operator--(int) -> Iterator
|
||||
{
|
||||
auto t { *this };
|
||||
--(*this);
|
||||
return t;
|
||||
}
|
||||
|
||||
friend auto operator==(Iterator a, Iterator b) -> bool
|
||||
{
|
||||
return a.m_cur == b.m_cur;
|
||||
}
|
||||
friend auto operator!=(Iterator a, Iterator b) -> bool
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
private:
|
||||
wl_list *m_cur { nullptr };
|
||||
wl_list *m_head { nullptr };
|
||||
};
|
||||
|
||||
struct ConstIterator {
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = T const;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T const *;
|
||||
using reference = T const &;
|
||||
|
||||
ConstIterator() = default;
|
||||
ConstIterator(wl_list const *cur, wl_list const *head)
|
||||
: m_cur(cur)
|
||||
, m_head(head)
|
||||
{
|
||||
}
|
||||
|
||||
auto operator*() const -> reference
|
||||
{
|
||||
return *detail::container_of<T, Member>(m_cur);
|
||||
}
|
||||
auto operator->() const -> pointer
|
||||
{
|
||||
return detail::container_of<T, Member>(m_cur);
|
||||
}
|
||||
|
||||
auto operator++() -> ConstIterator &
|
||||
{
|
||||
m_cur = m_cur->next;
|
||||
return *this;
|
||||
}
|
||||
auto operator++(int) -> ConstIterator
|
||||
{
|
||||
auto t { *this };
|
||||
++(*this);
|
||||
return t;
|
||||
}
|
||||
|
||||
auto operator--() -> ConstIterator &
|
||||
{
|
||||
m_cur = (m_cur == m_head) ? m_head->prev : m_cur->prev;
|
||||
return *this;
|
||||
}
|
||||
auto operator--(int) -> ConstIterator
|
||||
{
|
||||
auto t { *this };
|
||||
--(*this);
|
||||
return t;
|
||||
}
|
||||
|
||||
friend auto operator==(ConstIterator a, ConstIterator b) -> bool
|
||||
{
|
||||
return a.m_cur == b.m_cur;
|
||||
}
|
||||
friend auto operator!=(ConstIterator a, ConstIterator b) -> bool
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
private:
|
||||
wl_list const *m_cur { nullptr };
|
||||
wl_list const *m_head { nullptr };
|
||||
};
|
||||
|
||||
List()
|
||||
: m_head {}
|
||||
, m_external_head { nullptr }
|
||||
, m_should_cleanup { true }
|
||||
{
|
||||
wl_list_init(&m_head);
|
||||
}
|
||||
|
||||
explicit List(
|
||||
wl_list *existing_head, bool should_cleanup = false, bool init = false)
|
||||
: m_head {}
|
||||
, m_external_head { existing_head }
|
||||
, m_should_cleanup { should_cleanup }
|
||||
{
|
||||
if (init && m_external_head)
|
||||
wl_list_init(m_external_head);
|
||||
}
|
||||
|
||||
~List()
|
||||
{
|
||||
if (!m_should_cleanup)
|
||||
return;
|
||||
|
||||
clear();
|
||||
if (auto *h { head_ptr() }; h)
|
||||
wl_list_init(h);
|
||||
}
|
||||
|
||||
List(List const &) = delete;
|
||||
auto operator=(List const &) -> List & = delete;
|
||||
|
||||
List(List &&other) noexcept { move_from(other); }
|
||||
|
||||
auto operator=(List &&other) noexcept -> List &
|
||||
{
|
||||
if (this != &other) {
|
||||
this->~List();
|
||||
move_from(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline auto c_ptr() -> wl_list * { return head_ptr(); }
|
||||
inline auto c_ptr() const -> wl_list const * { return head_ptr(); }
|
||||
|
||||
auto empty() const noexcept -> bool { return wl_list_empty(head_ptr()); }
|
||||
auto length() const noexcept -> int { return wl_list_length(head_ptr()); }
|
||||
|
||||
auto push_front(T *elem) noexcept -> void
|
||||
{
|
||||
wl_list_insert(head_ptr(), &(elem->*Member));
|
||||
}
|
||||
|
||||
auto push_back(T *elem) noexcept -> void
|
||||
{
|
||||
auto *h { head_ptr() };
|
||||
wl_list_insert(h->prev, &(elem->*Member));
|
||||
}
|
||||
|
||||
auto remove(T *elem) noexcept -> void
|
||||
{
|
||||
wl_list_remove(&(elem->*Member));
|
||||
wl_list_init(&(elem->*Member));
|
||||
}
|
||||
|
||||
auto clear() noexcept -> void
|
||||
{
|
||||
auto *h { head_ptr() };
|
||||
while (!wl_list_empty(h)) {
|
||||
auto *node { h->next };
|
||||
wl_list_remove(node);
|
||||
wl_list_init(node);
|
||||
}
|
||||
}
|
||||
|
||||
auto begin() noexcept -> Iterator
|
||||
{
|
||||
auto *h { head_ptr() };
|
||||
return Iterator(h->next, h);
|
||||
}
|
||||
|
||||
auto end() noexcept -> Iterator
|
||||
{
|
||||
auto *h { head_ptr() };
|
||||
return Iterator(h, h);
|
||||
}
|
||||
|
||||
auto begin() const noexcept -> ConstIterator
|
||||
{
|
||||
auto const *h { head_ptr() };
|
||||
return ConstIterator(h->next, h);
|
||||
}
|
||||
|
||||
auto end() const noexcept -> ConstIterator
|
||||
{
|
||||
auto const *h { head_ptr() };
|
||||
return ConstIterator(h, h);
|
||||
}
|
||||
|
||||
private:
|
||||
auto head_ptr() noexcept -> wl_list *
|
||||
{
|
||||
return m_external_head ? m_external_head : &m_head;
|
||||
}
|
||||
|
||||
auto head_ptr() const noexcept -> wl_list const *
|
||||
{
|
||||
return m_external_head ? m_external_head : &m_head;
|
||||
}
|
||||
|
||||
auto move_from(List &other) noexcept -> void
|
||||
{
|
||||
m_head = other.m_head;
|
||||
m_external_head = other.m_external_head;
|
||||
m_should_cleanup = other.m_should_cleanup;
|
||||
|
||||
other.m_external_head = nullptr;
|
||||
other.m_should_cleanup = false;
|
||||
wl_list_init(&other.m_head);
|
||||
}
|
||||
|
||||
private:
|
||||
wl_list m_head {};
|
||||
wl_list *m_external_head { nullptr };
|
||||
bool m_should_cleanup { true };
|
||||
};
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
43
src/wayland/Region.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
struct Region {
|
||||
struct Box {
|
||||
std::int32_t x {};
|
||||
std::int32_t y {};
|
||||
std::int32_t width {};
|
||||
std::int32_t height {};
|
||||
};
|
||||
|
||||
explicit Region(wl_resource *resource)
|
||||
: m_resource(resource)
|
||||
{
|
||||
}
|
||||
|
||||
auto resource() const -> wl_resource * { return m_resource; }
|
||||
|
||||
auto add(std::int32_t x, std::int32_t y, std::int32_t width,
|
||||
std::int32_t height) -> void
|
||||
{
|
||||
m_boxes.push_back(Box { x, y, width, height });
|
||||
}
|
||||
|
||||
auto subtract(std::int32_t x, std::int32_t y, std::int32_t width,
|
||||
std::int32_t height) -> void
|
||||
{
|
||||
m_subtract_boxes.push_back(Box { x, y, width, height });
|
||||
}
|
||||
|
||||
private:
|
||||
wl_resource *m_resource {};
|
||||
std::vector<Box> m_boxes {};
|
||||
std::vector<Box> m_subtract_boxes {};
|
||||
};
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
96
src/wayland/Shm.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "Shm.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
ShmPool::ShmPool(int fd, std::size_t size, Logger &logger)
|
||||
: m_logger(logger)
|
||||
, m_fd(fd)
|
||||
, m_size(size)
|
||||
{
|
||||
m_data = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
|
||||
if (m_data == MAP_FAILED) {
|
||||
m_data = nullptr;
|
||||
m_logger.err("Failed to mmap shm pool: {}", std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
ShmPool::~ShmPool()
|
||||
{
|
||||
if (m_data) {
|
||||
munmap(m_data, m_size);
|
||||
}
|
||||
if (m_fd >= 0) {
|
||||
close(m_fd);
|
||||
}
|
||||
}
|
||||
|
||||
auto ShmPool::resize(std::size_t new_size) -> bool
|
||||
{
|
||||
if (!m_data) {
|
||||
return false;
|
||||
}
|
||||
void *new_data = mremap(m_data, m_size, new_size, MREMAP_MAYMOVE);
|
||||
if (new_data == MAP_FAILED) {
|
||||
m_logger.err("Failed to resize shm pool: {}", std::strerror(errno));
|
||||
return false;
|
||||
}
|
||||
m_data = new_data;
|
||||
m_size = new_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ShmPool::data() const -> std::byte *
|
||||
{
|
||||
return static_cast<std::byte *>(m_data);
|
||||
}
|
||||
|
||||
ShmBuffer::ShmBuffer(std::shared_ptr<ShmPool> pool, wl_resource *resource,
|
||||
std::int32_t offset, std::int32_t width, std::int32_t height,
|
||||
std::int32_t stride, std::uint32_t format)
|
||||
: pool(std::move(pool))
|
||||
, resource(resource)
|
||||
, offset(offset)
|
||||
, width(width)
|
||||
, height(height)
|
||||
, stride(stride)
|
||||
, format(format)
|
||||
{
|
||||
}
|
||||
|
||||
auto ShmBuffer::data() const -> std::byte *
|
||||
{
|
||||
if (!pool || !pool->data()) {
|
||||
return nullptr;
|
||||
}
|
||||
return pool->data() + offset;
|
||||
}
|
||||
|
||||
auto ShmBuffer::byte_size() const -> std::size_t
|
||||
{
|
||||
if (height <= 0 || stride <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<std::size_t>(height) * static_cast<std::size_t>(stride);
|
||||
}
|
||||
|
||||
auto shm_buffer_from_resource(wl_resource *resource)
|
||||
-> std::shared_ptr<ShmBuffer>
|
||||
{
|
||||
if (!resource) {
|
||||
return {};
|
||||
}
|
||||
auto *handle { static_cast<std::shared_ptr<ShmBuffer> *>(
|
||||
wl_resource_get_user_data(resource)) };
|
||||
if (!handle) {
|
||||
return {};
|
||||
}
|
||||
return *handle;
|
||||
}
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
48
src/wayland/Shm.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "../Logger.h"
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
struct ShmPool {
|
||||
explicit ShmPool(int fd, std::size_t size, Logger &logger);
|
||||
~ShmPool();
|
||||
|
||||
auto resize(std::size_t new_size) -> bool;
|
||||
auto data() const -> std::byte *;
|
||||
auto size() const -> std::size_t { return m_size; }
|
||||
|
||||
private:
|
||||
Logger &m_logger;
|
||||
int m_fd { -1 };
|
||||
std::size_t m_size { 0 };
|
||||
void *m_data { nullptr };
|
||||
};
|
||||
|
||||
struct ShmBuffer {
|
||||
ShmBuffer(std::shared_ptr<ShmPool> pool, wl_resource *resource,
|
||||
std::int32_t offset, std::int32_t width, std::int32_t height,
|
||||
std::int32_t stride, std::uint32_t format);
|
||||
|
||||
auto data() const -> std::byte *;
|
||||
auto byte_size() const -> std::size_t;
|
||||
|
||||
std::shared_ptr<ShmPool> pool;
|
||||
wl_resource *resource {};
|
||||
std::int32_t offset {};
|
||||
std::int32_t width {};
|
||||
std::int32_t height {};
|
||||
std::int32_t stride {};
|
||||
std::uint32_t format {};
|
||||
};
|
||||
|
||||
auto shm_buffer_from_resource(wl_resource *resource)
|
||||
-> std::shared_ptr<ShmBuffer>;
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
28
src/wayland/Signal.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
struct Signal {
|
||||
Signal(wl_signal *signal)
|
||||
: m_signal { std::move(signal) }
|
||||
{
|
||||
assert(m_signal);
|
||||
}
|
||||
~Signal() = default;
|
||||
|
||||
inline auto c_ptr() const -> wl_signal * { return m_signal; }
|
||||
|
||||
template<typename T = void> auto flush(T *data)
|
||||
{
|
||||
wl_signal_emit_mutable(m_signal, (void *)data);
|
||||
}
|
||||
|
||||
private:
|
||||
wl_signal *m_signal {};
|
||||
};
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
98
src/wayland/Surface.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "Surface.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "WaylandServer.h"
|
||||
|
||||
#include "wayland-server-protocol.h"
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
Surface::Surface(WaylandServer &server, wl_resource *resource)
|
||||
: m_server(server)
|
||||
, m_resource(resource)
|
||||
{
|
||||
m_server.register_surface(this);
|
||||
}
|
||||
|
||||
Surface::~Surface()
|
||||
{
|
||||
m_server.unregister_surface(this);
|
||||
for (auto *callback : m_frame_callbacks) {
|
||||
if (callback) {
|
||||
wl_callback_send_done(callback, m_server.now_ms());
|
||||
wl_resource_destroy(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Surface::attach(
|
||||
std::shared_ptr<ShmBuffer> buffer, std::int32_t x, std::int32_t y) -> void
|
||||
{
|
||||
m_pending_buffer = std::move(buffer);
|
||||
m_pending_offset_x = x;
|
||||
m_pending_offset_y = y;
|
||||
}
|
||||
|
||||
auto Surface::damage(std::int32_t, std::int32_t, std::int32_t, std::int32_t)
|
||||
-> void
|
||||
{
|
||||
}
|
||||
|
||||
auto Surface::damage_buffer(
|
||||
std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto Surface::frame(wl_resource *callback) -> void
|
||||
{
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
m_frame_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
auto Surface::commit() -> void
|
||||
{
|
||||
auto previous { m_current_buffer };
|
||||
m_current_buffer = m_pending_buffer;
|
||||
m_pending_buffer.reset();
|
||||
|
||||
if (previous && previous != m_current_buffer && previous->resource) {
|
||||
wl_buffer_send_release(previous->resource);
|
||||
}
|
||||
|
||||
if (!m_frame_callbacks.empty()) {
|
||||
auto callbacks { std::move(m_frame_callbacks) };
|
||||
auto done_time { m_server.now_ms() };
|
||||
for (auto *callback : callbacks) {
|
||||
if (callback) {
|
||||
wl_callback_send_done(callback, done_time);
|
||||
wl_resource_destroy(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Surface::set_opaque_region(std::shared_ptr<Region> region) -> void
|
||||
{
|
||||
m_opaque_region = std::move(region);
|
||||
}
|
||||
|
||||
auto Surface::set_input_region(std::shared_ptr<Region> region) -> void
|
||||
{
|
||||
m_input_region = std::move(region);
|
||||
}
|
||||
|
||||
auto Surface::set_buffer_transform(std::int32_t transform) -> void
|
||||
{
|
||||
m_buffer_transform = transform;
|
||||
}
|
||||
|
||||
auto Surface::set_buffer_scale(std::int32_t scale) -> void
|
||||
{
|
||||
m_buffer_scale = std::max(1, scale);
|
||||
}
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
53
src/wayland/Surface.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "Region.h"
|
||||
#include "Shm.h"
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
struct WaylandServer;
|
||||
|
||||
struct Surface {
|
||||
explicit Surface(WaylandServer &server, wl_resource *resource);
|
||||
~Surface();
|
||||
|
||||
auto resource() const -> wl_resource * { return m_resource; }
|
||||
auto current_buffer() const -> std::shared_ptr<ShmBuffer> const &
|
||||
{
|
||||
return m_current_buffer;
|
||||
}
|
||||
|
||||
auto attach(std::shared_ptr<ShmBuffer> buffer, std::int32_t x,
|
||||
std::int32_t y) -> void;
|
||||
auto damage(std::int32_t x, std::int32_t y, std::int32_t width,
|
||||
std::int32_t height) -> void;
|
||||
auto damage_buffer(std::int32_t x, std::int32_t y, std::int32_t width,
|
||||
std::int32_t height) -> void;
|
||||
auto frame(wl_resource *callback) -> void;
|
||||
auto commit() -> void;
|
||||
auto set_opaque_region(std::shared_ptr<Region> region) -> void;
|
||||
auto set_input_region(std::shared_ptr<Region> region) -> void;
|
||||
auto set_buffer_transform(std::int32_t transform) -> void;
|
||||
auto set_buffer_scale(std::int32_t scale) -> void;
|
||||
|
||||
private:
|
||||
WaylandServer &m_server;
|
||||
wl_resource *m_resource {};
|
||||
std::shared_ptr<ShmBuffer> m_pending_buffer {};
|
||||
std::shared_ptr<ShmBuffer> m_current_buffer {};
|
||||
std::shared_ptr<Region> m_opaque_region {};
|
||||
std::shared_ptr<Region> m_input_region {};
|
||||
std::vector<wl_resource *> m_frame_callbacks {};
|
||||
std::int32_t m_buffer_transform { 0 };
|
||||
std::int32_t m_buffer_scale { 1 };
|
||||
std::int32_t m_pending_offset_x { 0 };
|
||||
std::int32_t m_pending_offset_y { 0 };
|
||||
};
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
81
src/wayland/WaylandServer.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "WaylandServer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
WaylandServer::WaylandServer(Logger &logger)
|
||||
: m_logger(logger)
|
||||
{
|
||||
m_loop = wl_display_get_event_loop(m_display.c_ptr());
|
||||
if (!m_loop) {
|
||||
throw std::runtime_error("Failed to get Wayland event loop");
|
||||
}
|
||||
|
||||
auto *socket_name { wl_display_add_socket_auto(m_display.c_ptr()) };
|
||||
if (!socket_name) {
|
||||
throw std::runtime_error("Failed to create Wayland socket");
|
||||
}
|
||||
if (socket_name) {
|
||||
m_socket_name = socket_name;
|
||||
}
|
||||
|
||||
m_logger.info("Wayland listening on {}", m_socket_name);
|
||||
|
||||
m_compositor_global = create_compositor_global();
|
||||
m_shm_global = create_shm_global();
|
||||
m_xdg_wm_base_global = create_xdg_wm_base_global();
|
||||
}
|
||||
|
||||
WaylandServer::~WaylandServer() = default;
|
||||
|
||||
auto WaylandServer::dispatch() -> void
|
||||
{
|
||||
if (!m_loop) {
|
||||
return;
|
||||
}
|
||||
wl_event_loop_dispatch(m_loop, 0);
|
||||
}
|
||||
|
||||
auto WaylandServer::flush() -> void
|
||||
{
|
||||
wl_display_flush_clients(m_display.c_ptr());
|
||||
}
|
||||
|
||||
auto WaylandServer::now_ms() const -> std::uint32_t
|
||||
{
|
||||
using Clock = std::chrono::steady_clock;
|
||||
auto now { Clock::now().time_since_epoch() };
|
||||
auto ms {
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(now).count()
|
||||
};
|
||||
return static_cast<std::uint32_t>(ms);
|
||||
}
|
||||
|
||||
auto WaylandServer::register_surface(Surface *surface) -> void
|
||||
{
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
m_surfaces.push_back(surface);
|
||||
}
|
||||
|
||||
auto WaylandServer::unregister_surface(Surface *surface) -> void
|
||||
{
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
auto it { std::remove(m_surfaces.begin(), m_surfaces.end(), surface) };
|
||||
m_surfaces.erase(it, m_surfaces.end());
|
||||
}
|
||||
|
||||
auto WaylandServer::surfaces() const -> std::span<Surface *const>
|
||||
{
|
||||
return { m_surfaces.data(), m_surfaces.size() };
|
||||
}
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
#include "Surface.h"
|
||||
50
src/wayland/WaylandServer.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "../Logger.h"
|
||||
|
||||
#include "Display.h"
|
||||
#include "Global.h"
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
|
||||
struct Surface;
|
||||
|
||||
struct WaylandServer {
|
||||
explicit WaylandServer(Logger &logger);
|
||||
~WaylandServer();
|
||||
|
||||
auto display() -> Display & { return m_display; }
|
||||
auto logger() -> Logger & { return m_logger; }
|
||||
auto socket_name() const -> std::string_view { return m_socket_name; }
|
||||
auto dispatch() -> void;
|
||||
auto flush() -> void;
|
||||
auto now_ms() const -> std::uint32_t;
|
||||
auto register_surface(Surface *surface) -> void;
|
||||
auto unregister_surface(Surface *surface) -> void;
|
||||
auto surfaces() const -> std::span<Surface *const>;
|
||||
|
||||
private:
|
||||
auto create_compositor_global() -> std::unique_ptr<Global>;
|
||||
auto create_shm_global() -> std::unique_ptr<Global>;
|
||||
auto create_xdg_wm_base_global() -> std::unique_ptr<Global>;
|
||||
|
||||
Logger &m_logger;
|
||||
Display m_display {};
|
||||
wl_event_loop *m_loop { nullptr };
|
||||
std::string m_socket_name {};
|
||||
std::unique_ptr<Global> m_compositor_global {};
|
||||
std::unique_ptr<Global> m_shm_global {};
|
||||
std::unique_ptr<Global> m_xdg_wm_base_global {};
|
||||
std::vector<Surface *> m_surfaces {};
|
||||
};
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
254
src/wayland/protocols/CompositorProtocol.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "wayland-server-protocol.h"
|
||||
|
||||
#include "../Region.h"
|
||||
#include "../Shm.h"
|
||||
#include "../Surface.h"
|
||||
#include "../WaylandServer.h"
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
namespace {
|
||||
|
||||
constexpr std::uint32_t COMPOSITOR_VERSION = 4;
|
||||
|
||||
auto resource_version(wl_resource *resource) -> std::uint32_t
|
||||
{
|
||||
return std::min<std::uint32_t>(
|
||||
wl_resource_get_version(resource), COMPOSITOR_VERSION);
|
||||
}
|
||||
|
||||
auto region_from_resource(wl_resource *resource) -> std::shared_ptr<Region>
|
||||
{
|
||||
if (!resource) {
|
||||
return {};
|
||||
}
|
||||
auto *handle { static_cast<std::shared_ptr<Region> *>(
|
||||
wl_resource_get_user_data(resource)) };
|
||||
if (!handle) {
|
||||
return {};
|
||||
}
|
||||
return *handle;
|
||||
}
|
||||
|
||||
void region_destroy(wl_resource *resource)
|
||||
{
|
||||
auto *handle { static_cast<std::shared_ptr<Region> *>(
|
||||
wl_resource_get_user_data(resource)) };
|
||||
delete handle;
|
||||
}
|
||||
|
||||
void region_handle_destroy(wl_client *, wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
void region_handle_add(wl_client *, wl_resource *resource, std::int32_t x,
|
||||
std::int32_t y, std::int32_t width, std::int32_t height)
|
||||
{
|
||||
if (auto region { region_from_resource(resource) }) {
|
||||
region->add(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void region_handle_subtract(wl_client *, wl_resource *resource, std::int32_t x,
|
||||
std::int32_t y, std::int32_t width, std::int32_t height)
|
||||
{
|
||||
if (auto region { region_from_resource(resource) }) {
|
||||
region->subtract(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
struct wl_region_interface const REGION_INTERFACE = {
|
||||
.destroy = region_handle_destroy,
|
||||
.add = region_handle_add,
|
||||
.subtract = region_handle_subtract,
|
||||
};
|
||||
|
||||
auto surface_from_resource(wl_resource *resource) -> Surface *
|
||||
{
|
||||
return static_cast<Surface *>(wl_resource_get_user_data(resource));
|
||||
}
|
||||
|
||||
void surface_destroy_resource(wl_resource *resource)
|
||||
{
|
||||
auto *surface { surface_from_resource(resource) };
|
||||
delete surface;
|
||||
}
|
||||
|
||||
void surface_handle_destroy(wl_client *, wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
void surface_handle_attach(wl_client *, wl_resource *resource,
|
||||
wl_resource *buffer_resource, std::int32_t x, std::int32_t y)
|
||||
{
|
||||
auto *surface { surface_from_resource(resource) };
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
surface->attach(shm_buffer_from_resource(buffer_resource), x, y);
|
||||
}
|
||||
|
||||
void surface_handle_damage(wl_client *, wl_resource *resource, std::int32_t x,
|
||||
std::int32_t y, std::int32_t width, std::int32_t height)
|
||||
{
|
||||
if (auto *surface { surface_from_resource(resource) }) {
|
||||
surface->damage(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void surface_handle_frame(
|
||||
wl_client *client, wl_resource *resource, std::uint32_t callback_id)
|
||||
{
|
||||
auto *surface { surface_from_resource(resource) };
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
auto version { wl_resource_get_version(resource) };
|
||||
auto *callback_resource { wl_resource_create(
|
||||
client, &wl_callback_interface, version, callback_id) };
|
||||
if (!callback_resource) {
|
||||
return;
|
||||
}
|
||||
wl_resource_set_implementation(
|
||||
callback_resource, nullptr, nullptr, nullptr);
|
||||
surface->frame(callback_resource);
|
||||
}
|
||||
|
||||
void surface_handle_set_opaque_region(
|
||||
wl_client *, wl_resource *resource, wl_resource *region_resource)
|
||||
{
|
||||
if (auto *surface { surface_from_resource(resource) }) {
|
||||
surface->set_opaque_region(region_from_resource(region_resource));
|
||||
}
|
||||
}
|
||||
|
||||
void surface_handle_set_input_region(
|
||||
wl_client *, wl_resource *resource, wl_resource *region_resource)
|
||||
{
|
||||
if (auto *surface { surface_from_resource(resource) }) {
|
||||
surface->set_input_region(region_from_resource(region_resource));
|
||||
}
|
||||
}
|
||||
|
||||
void surface_handle_commit(wl_client *, wl_resource *resource)
|
||||
{
|
||||
if (auto *surface { surface_from_resource(resource) }) {
|
||||
surface->commit();
|
||||
}
|
||||
}
|
||||
|
||||
void surface_handle_set_buffer_transform(
|
||||
wl_client *, wl_resource *resource, std::int32_t transform)
|
||||
{
|
||||
if (auto *surface { surface_from_resource(resource) }) {
|
||||
surface->set_buffer_transform(transform);
|
||||
}
|
||||
}
|
||||
|
||||
void surface_handle_set_buffer_scale(
|
||||
wl_client *, wl_resource *resource, std::int32_t scale)
|
||||
{
|
||||
if (auto *surface { surface_from_resource(resource) }) {
|
||||
surface->set_buffer_scale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
void surface_handle_damage_buffer(wl_client *, wl_resource *resource,
|
||||
std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
|
||||
{
|
||||
if (auto *surface { surface_from_resource(resource) }) {
|
||||
surface->damage_buffer(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void surface_handle_offset(
|
||||
wl_client *, wl_resource *resource, std::int32_t x, std::int32_t y)
|
||||
{
|
||||
(void)resource;
|
||||
(void)x;
|
||||
(void)y;
|
||||
}
|
||||
|
||||
struct wl_surface_interface const SURFACE_INTERFACE = {
|
||||
.destroy = surface_handle_destroy,
|
||||
.attach = surface_handle_attach,
|
||||
.damage = surface_handle_damage,
|
||||
.frame = surface_handle_frame,
|
||||
.set_opaque_region = surface_handle_set_opaque_region,
|
||||
.set_input_region = surface_handle_set_input_region,
|
||||
.commit = surface_handle_commit,
|
||||
.set_buffer_transform = surface_handle_set_buffer_transform,
|
||||
.set_buffer_scale = surface_handle_set_buffer_scale,
|
||||
.damage_buffer = surface_handle_damage_buffer,
|
||||
.offset = surface_handle_offset,
|
||||
};
|
||||
|
||||
void compositor_handle_create_surface(
|
||||
wl_client *client, wl_resource *resource, std::uint32_t id)
|
||||
{
|
||||
auto *server { static_cast<WaylandServer *>(
|
||||
wl_resource_get_user_data(resource)) };
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
auto version { resource_version(resource) };
|
||||
auto *surface_resource { wl_resource_create(
|
||||
client, &wl_surface_interface, version, id) };
|
||||
if (!surface_resource) {
|
||||
return;
|
||||
}
|
||||
auto *surface { new Surface(*server, surface_resource) };
|
||||
wl_resource_set_implementation(surface_resource, &SURFACE_INTERFACE,
|
||||
surface, surface_destroy_resource);
|
||||
}
|
||||
|
||||
void compositor_handle_create_region(
|
||||
wl_client *client, wl_resource *resource, std::uint32_t id)
|
||||
{
|
||||
auto version { wl_resource_get_version(resource) };
|
||||
auto *region_resource { wl_resource_create(
|
||||
client, &wl_region_interface, version, id) };
|
||||
if (!region_resource) {
|
||||
return;
|
||||
}
|
||||
auto region { std::make_shared<Region>(region_resource) };
|
||||
auto *handle { new std::shared_ptr<Region>(std::move(region)) };
|
||||
wl_resource_set_implementation(
|
||||
region_resource, ®ION_INTERFACE, handle, region_destroy);
|
||||
}
|
||||
|
||||
struct wl_compositor_interface const COMPOSITOR_INTERFACE = {
|
||||
.create_surface = compositor_handle_create_surface,
|
||||
.create_region = compositor_handle_create_region,
|
||||
};
|
||||
|
||||
void bind_compositor(
|
||||
wl_client *client, void *data, std::uint32_t version, std::uint32_t id)
|
||||
{
|
||||
auto *server { static_cast<WaylandServer *>(data) };
|
||||
auto *resource { wl_resource_create(client, &wl_compositor_interface,
|
||||
std::min(version, COMPOSITOR_VERSION), id) };
|
||||
if (!resource) {
|
||||
return;
|
||||
}
|
||||
wl_resource_set_implementation(
|
||||
resource, &COMPOSITOR_INTERFACE, server, nullptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto WaylandServer::create_compositor_global() -> std::unique_ptr<Global>
|
||||
{
|
||||
return std::make_unique<Global>(display(), &wl_compositor_interface,
|
||||
COMPOSITOR_VERSION, this, bind_compositor);
|
||||
}
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
171
src/wayland/protocols/ShmProtocol.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "wayland-server-protocol.h"
|
||||
|
||||
#include "../Shm.h"
|
||||
#include "../WaylandServer.h"
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
namespace {
|
||||
|
||||
constexpr std::uint32_t SHM_VERSION = 2;
|
||||
|
||||
auto shm_pool_from_resource(wl_resource *resource) -> std::shared_ptr<ShmPool>
|
||||
{
|
||||
auto *handle { static_cast<std::shared_ptr<ShmPool> *>(
|
||||
wl_resource_get_user_data(resource)) };
|
||||
if (!handle) {
|
||||
return {};
|
||||
}
|
||||
return *handle;
|
||||
}
|
||||
|
||||
void shm_pool_destroy_resource(wl_resource *resource)
|
||||
{
|
||||
auto *handle { static_cast<std::shared_ptr<ShmPool> *>(
|
||||
wl_resource_get_user_data(resource)) };
|
||||
delete handle;
|
||||
}
|
||||
|
||||
void shm_buffer_destroy_resource(wl_resource *resource)
|
||||
{
|
||||
auto *handle { static_cast<std::shared_ptr<ShmBuffer> *>(
|
||||
wl_resource_get_user_data(resource)) };
|
||||
delete handle;
|
||||
}
|
||||
|
||||
void shm_pool_handle_destroy(wl_client *, wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
void shm_pool_handle_resize(
|
||||
wl_client *, wl_resource *resource, std::int32_t size)
|
||||
{
|
||||
auto pool { shm_pool_from_resource(resource) };
|
||||
if (!pool) {
|
||||
return;
|
||||
}
|
||||
if (size <= 0) {
|
||||
return;
|
||||
}
|
||||
pool->resize(static_cast<std::size_t>(size));
|
||||
}
|
||||
|
||||
void shm_handle_release(wl_client *, wl_resource *) { }
|
||||
|
||||
void shm_pool_handle_create_buffer(wl_client *client, wl_resource *resource,
|
||||
std::uint32_t id, std::int32_t offset, std::int32_t width,
|
||||
std::int32_t height, std::int32_t stride, std::uint32_t format)
|
||||
{
|
||||
auto pool { shm_pool_from_resource(resource) };
|
||||
if (!pool) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (width <= 0 || height <= 0 || stride <= 0 || offset < 0) {
|
||||
wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE,
|
||||
"Invalid shm buffer geometry");
|
||||
return;
|
||||
}
|
||||
|
||||
if (format != WL_SHM_FORMAT_XRGB8888 && format != WL_SHM_FORMAT_ARGB8888) {
|
||||
wl_resource_post_error(
|
||||
resource, WL_SHM_ERROR_INVALID_FORMAT, "Unsupported shm format");
|
||||
return;
|
||||
}
|
||||
|
||||
auto required { static_cast<std::size_t>(offset)
|
||||
+ static_cast<std::size_t>(height) * static_cast<std::size_t>(stride) };
|
||||
if (required > pool->size()) {
|
||||
wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE,
|
||||
"Shm buffer size out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
auto *buffer_resource { wl_resource_create(
|
||||
client, &wl_buffer_interface, 1, id) };
|
||||
if (!buffer_resource) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto buffer { std::make_shared<ShmBuffer>(
|
||||
pool, buffer_resource, offset, width, height, stride, format) };
|
||||
auto *handle { new std::shared_ptr<ShmBuffer>(std::move(buffer)) };
|
||||
wl_resource_set_implementation(
|
||||
buffer_resource, nullptr, handle, shm_buffer_destroy_resource);
|
||||
}
|
||||
|
||||
struct wl_shm_pool_interface const SHM_POOL_INTERFACE = {
|
||||
.create_buffer = shm_pool_handle_create_buffer,
|
||||
.destroy = shm_pool_handle_destroy,
|
||||
.resize = shm_pool_handle_resize,
|
||||
};
|
||||
|
||||
void shm_handle_create_pool(wl_client *client, wl_resource *resource,
|
||||
std::uint32_t id, int fd, std::int32_t size)
|
||||
{
|
||||
auto *server
|
||||
= static_cast<WaylandServer *>(wl_resource_get_user_data(resource));
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
if (size <= 0) {
|
||||
wl_resource_post_error(
|
||||
resource, WL_SHM_ERROR_INVALID_STRIDE, "Invalid shm pool size");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
auto pool { std::make_shared<ShmPool>(
|
||||
fd, static_cast<std::size_t>(size), server->logger()) };
|
||||
if (!pool->data()) {
|
||||
wl_resource_post_error(
|
||||
resource, WL_SHM_ERROR_INVALID_FD, "Failed to mmap shm pool");
|
||||
return;
|
||||
}
|
||||
|
||||
auto *pool_resource
|
||||
= wl_resource_create(client, &wl_shm_pool_interface, 1, id);
|
||||
if (!pool_resource) {
|
||||
return;
|
||||
}
|
||||
auto *handle { new std::shared_ptr<ShmPool>(std::move(pool)) };
|
||||
wl_resource_set_implementation(
|
||||
pool_resource, &SHM_POOL_INTERFACE, handle, shm_pool_destroy_resource);
|
||||
}
|
||||
|
||||
struct wl_shm_interface const SHM_INTERFACE = {
|
||||
.create_pool = shm_handle_create_pool,
|
||||
.release = shm_handle_release,
|
||||
};
|
||||
|
||||
void bind_shm(
|
||||
wl_client *client, void *data, std::uint32_t version, std::uint32_t id)
|
||||
{
|
||||
auto *server { static_cast<WaylandServer *>(data) };
|
||||
auto *resource { wl_resource_create(
|
||||
client, &wl_shm_interface, std::min(version, SHM_VERSION), id) };
|
||||
if (!resource) {
|
||||
return;
|
||||
}
|
||||
wl_resource_set_implementation(resource, &SHM_INTERFACE, server, nullptr);
|
||||
wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888);
|
||||
wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto WaylandServer::create_shm_global() -> std::unique_ptr<Global>
|
||||
{
|
||||
return std::make_unique<Global>(
|
||||
display(), &wl_shm_interface, SHM_VERSION, this, bind_shm);
|
||||
}
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
327
src/wayland/protocols/XdgShellProtocol.cpp
Normal file
@@ -0,0 +1,327 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "xdg-shell-server-protocol.h"
|
||||
|
||||
#include "../WaylandServer.h"
|
||||
|
||||
namespace Lunar::Wayland {
|
||||
namespace {
|
||||
|
||||
constexpr std::uint32_t XDG_WM_BASE_VERSION = 7;
|
||||
|
||||
struct XdgSurface {
|
||||
WaylandServer &server;
|
||||
wl_resource *resource {};
|
||||
wl_resource *surface_resource {};
|
||||
wl_resource *toplevel_resource {};
|
||||
std::uint32_t last_serial { 0 };
|
||||
};
|
||||
|
||||
struct XdgToplevel {
|
||||
WaylandServer &server;
|
||||
wl_resource *resource {};
|
||||
XdgSurface *surface {};
|
||||
};
|
||||
|
||||
extern struct xdg_toplevel_interface const XDG_TOPLEVEL_INTERFACE_IMPL;
|
||||
|
||||
auto xdg_surface_from_resource(wl_resource *resource) -> XdgSurface *
|
||||
{
|
||||
return static_cast<XdgSurface *>(wl_resource_get_user_data(resource));
|
||||
}
|
||||
|
||||
auto xdg_toplevel_from_resource(wl_resource *resource) -> XdgToplevel *
|
||||
{
|
||||
return static_cast<XdgToplevel *>(wl_resource_get_user_data(resource));
|
||||
}
|
||||
|
||||
auto send_configure(XdgSurface &surface) -> void
|
||||
{
|
||||
std::uint32_t serial = surface.server.display().next_serial();
|
||||
if (surface.toplevel_resource) {
|
||||
wl_array states;
|
||||
wl_array_init(&states);
|
||||
xdg_toplevel_send_configure(surface.toplevel_resource, 0, 0, &states);
|
||||
wl_array_release(&states);
|
||||
}
|
||||
surface.last_serial = serial;
|
||||
xdg_surface_send_configure(surface.resource, serial);
|
||||
}
|
||||
|
||||
auto xdg_positioner_handle_destroy(wl_client *, wl_resource *resource) -> void
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
auto xdg_positioner_handle_set_size(
|
||||
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_positioner_handle_set_anchor_rect(wl_client *, wl_resource *,
|
||||
std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_positioner_handle_set_anchor(wl_client *, wl_resource *, std::uint32_t)
|
||||
-> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_positioner_handle_set_gravity(
|
||||
wl_client *, wl_resource *, std::uint32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_positioner_handle_set_constraint_adjustment(
|
||||
wl_client *, wl_resource *, std::uint32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_positioner_handle_set_offset(
|
||||
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_positioner_handle_set_reactive(wl_client *, wl_resource *) -> void { }
|
||||
|
||||
auto xdg_positioner_handle_set_parent_size(
|
||||
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_positioner_handle_set_parent_configure(
|
||||
wl_client *, wl_resource *, std::uint32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
struct xdg_positioner_interface const XDG_POSITIONER_INTERFACE_IMPL = {
|
||||
.destroy = xdg_positioner_handle_destroy,
|
||||
.set_size = xdg_positioner_handle_set_size,
|
||||
.set_anchor_rect = xdg_positioner_handle_set_anchor_rect,
|
||||
.set_anchor = xdg_positioner_handle_set_anchor,
|
||||
.set_gravity = xdg_positioner_handle_set_gravity,
|
||||
.set_constraint_adjustment
|
||||
= xdg_positioner_handle_set_constraint_adjustment,
|
||||
.set_offset = xdg_positioner_handle_set_offset,
|
||||
.set_reactive = xdg_positioner_handle_set_reactive,
|
||||
.set_parent_size = xdg_positioner_handle_set_parent_size,
|
||||
.set_parent_configure = xdg_positioner_handle_set_parent_configure,
|
||||
};
|
||||
|
||||
auto xdg_surface_destroy_resource(wl_resource *resource) -> void
|
||||
{
|
||||
auto *surface { xdg_surface_from_resource(resource) };
|
||||
delete surface;
|
||||
}
|
||||
|
||||
auto xdg_surface_handle_destroy(wl_client *, wl_resource *resource) -> void
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
auto xdg_surface_handle_get_toplevel(
|
||||
wl_client *client, wl_resource *resource, std::uint32_t id) -> void
|
||||
{
|
||||
auto *surface { xdg_surface_from_resource(resource) };
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
auto version { wl_resource_get_version(resource) };
|
||||
auto *toplevel_resource { wl_resource_create(
|
||||
client, &::xdg_toplevel_interface, version, id) };
|
||||
if (!toplevel_resource) {
|
||||
return;
|
||||
}
|
||||
auto *toplevel { new XdgToplevel {
|
||||
surface->server, toplevel_resource, surface } };
|
||||
surface->toplevel_resource = toplevel_resource;
|
||||
wl_resource_set_implementation(toplevel_resource,
|
||||
&XDG_TOPLEVEL_INTERFACE_IMPL, toplevel, [](wl_resource *res) {
|
||||
auto *tl { xdg_toplevel_from_resource(res) };
|
||||
if (tl && tl->surface && tl->surface->toplevel_resource == res) {
|
||||
tl->surface->toplevel_resource = nullptr;
|
||||
}
|
||||
delete tl;
|
||||
});
|
||||
|
||||
send_configure(*surface);
|
||||
}
|
||||
|
||||
auto xdg_surface_handle_get_popup(wl_client *, wl_resource *, std::uint32_t,
|
||||
wl_resource *, wl_resource *) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_surface_handle_set_window_geometry(wl_client *, wl_resource *,
|
||||
std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_surface_handle_ack_configure(
|
||||
wl_client *, wl_resource *resource, std::uint32_t serial) -> void
|
||||
{
|
||||
if (auto *surface { xdg_surface_from_resource(resource) }) {
|
||||
surface->last_serial = serial;
|
||||
}
|
||||
}
|
||||
|
||||
struct xdg_surface_interface const XDG_SURFACE_INTERFACE_IMPL = {
|
||||
.destroy = xdg_surface_handle_destroy,
|
||||
.get_toplevel = xdg_surface_handle_get_toplevel,
|
||||
.get_popup = xdg_surface_handle_get_popup,
|
||||
.set_window_geometry = xdg_surface_handle_set_window_geometry,
|
||||
.ack_configure = xdg_surface_handle_ack_configure,
|
||||
};
|
||||
|
||||
auto xdg_toplevel_handle_destroy(wl_client *, wl_resource *resource) -> void
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_set_parent(wl_client *, wl_resource *, wl_resource *)
|
||||
-> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_set_title(wl_client *, wl_resource *, char const *)
|
||||
-> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_set_app_id(wl_client *, wl_resource *, char const *)
|
||||
-> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_show_window_menu(wl_client *, wl_resource *,
|
||||
wl_resource *, std::uint32_t, std::int32_t, std::int32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_move(
|
||||
wl_client *, wl_resource *, wl_resource *, std::uint32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_resize(wl_client *, wl_resource *, wl_resource *,
|
||||
std::uint32_t, std::uint32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_set_max_size(
|
||||
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_set_min_size(
|
||||
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_set_maximized(wl_client *, wl_resource *) -> void { }
|
||||
|
||||
auto xdg_toplevel_handle_unset_maximized(wl_client *, wl_resource *) -> void { }
|
||||
|
||||
auto xdg_toplevel_handle_set_fullscreen(
|
||||
wl_client *, wl_resource *, wl_resource *) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_unset_fullscreen(wl_client *, wl_resource *) -> void {
|
||||
}
|
||||
|
||||
auto xdg_toplevel_handle_set_minimized(wl_client *, wl_resource *) -> void { }
|
||||
|
||||
struct xdg_toplevel_interface const XDG_TOPLEVEL_INTERFACE_IMPL = {
|
||||
.destroy = xdg_toplevel_handle_destroy,
|
||||
.set_parent = xdg_toplevel_handle_set_parent,
|
||||
.set_title = xdg_toplevel_handle_set_title,
|
||||
.set_app_id = xdg_toplevel_handle_set_app_id,
|
||||
.show_window_menu = xdg_toplevel_handle_show_window_menu,
|
||||
.move = xdg_toplevel_handle_move,
|
||||
.resize = xdg_toplevel_handle_resize,
|
||||
.set_max_size = xdg_toplevel_handle_set_max_size,
|
||||
.set_min_size = xdg_toplevel_handle_set_min_size,
|
||||
.set_maximized = xdg_toplevel_handle_set_maximized,
|
||||
.unset_maximized = xdg_toplevel_handle_unset_maximized,
|
||||
.set_fullscreen = xdg_toplevel_handle_set_fullscreen,
|
||||
.unset_fullscreen = xdg_toplevel_handle_unset_fullscreen,
|
||||
.set_minimized = xdg_toplevel_handle_set_minimized,
|
||||
};
|
||||
|
||||
auto xdg_wm_base_handle_destroy(wl_client *, wl_resource *resource) -> void
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
auto xdg_wm_base_handle_create_positioner(
|
||||
wl_client *client, wl_resource *resource, std::uint32_t id) -> void
|
||||
{
|
||||
auto version { wl_resource_get_version(resource) };
|
||||
auto *positioner { wl_resource_create(
|
||||
client, &::xdg_positioner_interface, version, id) };
|
||||
if (!positioner) {
|
||||
return;
|
||||
}
|
||||
wl_resource_set_implementation(
|
||||
positioner, &XDG_POSITIONER_INTERFACE_IMPL, nullptr, nullptr);
|
||||
}
|
||||
|
||||
auto xdg_wm_base_handle_get_xdg_surface(wl_client *client,
|
||||
wl_resource *resource, std::uint32_t id, wl_resource *surface_resource)
|
||||
-> void
|
||||
{
|
||||
auto *server
|
||||
= static_cast<WaylandServer *>(wl_resource_get_user_data(resource));
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
auto version { wl_resource_get_version(resource) };
|
||||
auto *xdg_surface_resource { wl_resource_create(
|
||||
client, &::xdg_surface_interface, version, id) };
|
||||
if (!xdg_surface_resource) {
|
||||
return;
|
||||
}
|
||||
auto *surface { new XdgSurface {
|
||||
*server, xdg_surface_resource, surface_resource, nullptr, 0 } };
|
||||
wl_resource_set_implementation(xdg_surface_resource,
|
||||
&XDG_SURFACE_INTERFACE_IMPL, surface, xdg_surface_destroy_resource);
|
||||
}
|
||||
|
||||
auto xdg_wm_base_handle_pong(wl_client *, wl_resource *, std::uint32_t) -> void
|
||||
{
|
||||
}
|
||||
|
||||
struct xdg_wm_base_interface const XDG_WM_BASE_INTERFACE_IMPL = {
|
||||
.destroy = xdg_wm_base_handle_destroy,
|
||||
.create_positioner = xdg_wm_base_handle_create_positioner,
|
||||
.get_xdg_surface = xdg_wm_base_handle_get_xdg_surface,
|
||||
.pong = xdg_wm_base_handle_pong,
|
||||
};
|
||||
|
||||
auto bind_xdg_wm_base(wl_client *client, void *data, std::uint32_t version,
|
||||
std::uint32_t id) -> void
|
||||
{
|
||||
auto *server { static_cast<WaylandServer *>(data) };
|
||||
auto *resource { wl_resource_create(client, &::xdg_wm_base_interface,
|
||||
std::min(version, XDG_WM_BASE_VERSION), id) };
|
||||
if (!resource) {
|
||||
return;
|
||||
}
|
||||
wl_resource_set_implementation(
|
||||
resource, &XDG_WM_BASE_INTERFACE_IMPL, server, nullptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto WaylandServer::create_xdg_wm_base_global() -> std::unique_ptr<Global>
|
||||
{
|
||||
return std::make_unique<Global>(display(), &::xdg_wm_base_interface,
|
||||
XDG_WM_BASE_VERSION, this, bind_xdg_wm_base);
|
||||
}
|
||||
|
||||
} // namespace Lunar::Wayland
|
||||
2
thirdparty/smath
vendored
32
thirdparty/stb/.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
Pull Requests and Issues are both welcome.
|
||||
|
||||
# Responsiveness
|
||||
|
||||
General priority order is:
|
||||
|
||||
* Crashes
|
||||
* Security issues in stb_image
|
||||
* Bugs
|
||||
* Security concerns in other libs
|
||||
* Warnings
|
||||
* Enhancements (new features, performance improvement, etc)
|
||||
|
||||
Pull requests get priority over Issues. Some pull requests I take
|
||||
as written; some I modify myself; some I will request changes before
|
||||
accepting them. Because I've ended up supporting a lot of libraries
|
||||
(20 as I write this, with more on the way), I am somewhat slow to
|
||||
address things. Many issues have been around for a long time.
|
||||
|
||||
# Pull requests
|
||||
|
||||
* Make sure you're using a special branch just for this pull request. (Sometimes people unknowingly use a default branch, then later update that branch, which updates the pull request with the other changes if it hasn't been merged yet.)
|
||||
* Do NOT update the version number in the file. (This just causes conflicts.)
|
||||
* Do add your name to the list of contributors. (Don't worry about the formatting.) I'll try to remember to add it if you don't, but I sometimes forget as it's an extra step.
|
||||
* Your change needs to compile as both C and C++. Pre-C99 compilers should be supported (e.g. declare at start of block)
|
||||
|
||||
# Specific libraries
|
||||
|
||||
I generally do not want new file formats for stb_image because
|
||||
we are trying to improve its security, so increasing its attack
|
||||
surface is counter-productive.
|
||||
|
||||
15
thirdparty/stb/.github/ISSUE_TEMPLATE/1-stb_image-doesn-t-load-specific-image-correctly.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: stb_image Doesn't Load Specific Image Correctly
|
||||
about: if an image displays wrong in your program, and you've verified stb_image is
|
||||
the problem
|
||||
title: ''
|
||||
labels: 1 stb_image
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
1. **Confirm that, after loading the image with stbi_load, you've immediately written it out with stbi_write_png or similar, and that version of the image is also wrong.** If it is correct when written out, the problem is not in stb_image. If it displays wrong in a program you're writing, it's probably your display code. For example, people writing OpenGL programs frequently do not upload or display the image correctly and assume stb_image is at fault even though writing out the image demonstrates that it loads correctly.
|
||||
|
||||
2. *Provide an image that does not load correctly using stb_image* so we can reproduce the problem.
|
||||
|
||||
3. *Provide an image or description of what part of the image is incorrect and how* so we can be sure we've reproduced the problem correctly.
|
||||
24
thirdparty/stb/.github/ISSUE_TEMPLATE/2-bug_report.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: if you're having trouble using a library, try the support forum instead
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
20
thirdparty/stb/.github/ISSUE_TEMPLATE/3-feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 4 enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
5
thirdparty/stb/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: support forum
|
||||
url: https://github.com/nothings/stb/discussions/categories/q-a
|
||||
about: having trouble using an stb library? don't create an issue, post in the forum
|
||||
6
thirdparty/stb/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
* Delete this list before clicking CREATE PULL REQUEST
|
||||
* Make sure you're using a special branch just for this pull request. (Sometimes people unknowingly use a default branch, then later update that branch, which updates the pull request with the other changes if it hasn't been merged yet.)
|
||||
* Do NOT update the version number in the file. (This just causes conflicts.)
|
||||
* Do add your name to the list of contributors. (Don't worry about the formatting.) I'll try to remember to add it if you don't, but I sometimes forget as it's an extra step.
|
||||
|
||||
If you get something above wrong, don't fret it, it's not the end of the world.
|
||||
23
thirdparty/stb/.github/workflows/ci-fuzz.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: CIFuzz
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'stb'
|
||||
dry-run: false
|
||||
- name: Run Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'stb'
|
||||
fuzz-seconds: 900
|
||||
dry-run: false
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v1
|
||||
if: failure()
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
3
thirdparty/stb/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
8
thirdparty/stb/.travis.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
language: C
|
||||
arch:
|
||||
- AMD64
|
||||
- ppc64le
|
||||
install: true
|
||||
script:
|
||||
- cd tests
|
||||
- make all
|
||||
37
thirdparty/stb/LICENSE
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
184
thirdparty/stb/README.md
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
<!--- THIS FILE IS AUTOMATICALLY GENERATED, DO NOT CHANGE IT BY HAND --->
|
||||
|
||||
stb
|
||||
===
|
||||
|
||||
single-file public domain (or MIT licensed) libraries for C/C++
|
||||
|
||||
# This project discusses security-relevant bugs in public in Github Issues and Pull Requests, and it may take significant time for security fixes to be implemented or merged. If this poses an unreasonable risk to your project, do not use stb libraries.
|
||||
|
||||
Noteworthy:
|
||||
|
||||
* image loader: [stb_image.h](stb_image.h)
|
||||
* image writer: [stb_image_write.h](stb_image_write.h)
|
||||
* image resizer: [stb_image_resize2.h](stb_image_resize2.h)
|
||||
* font text rasterizer: [stb_truetype.h](stb_truetype.h)
|
||||
* typesafe containers: [stb_ds.h](stb_ds.h)
|
||||
|
||||
Most libraries by stb, except: stb_dxt by Fabian "ryg" Giesen, original stb_image_resize
|
||||
by Jorge L. "VinoBS" Rodriguez, and stb_image_resize2 and stb_sprintf by Jeff Roberts.
|
||||
|
||||
<a name="stb_libs"></a>
|
||||
|
||||
library | latest version | category | LoC | description
|
||||
--------------------- | ---- | -------- | --- | --------------------------------
|
||||
**[stb_vorbis.c](stb_vorbis.c)** | 1.22 | audio | 5584 | decode ogg vorbis files from file/memory to float/16-bit signed output
|
||||
**[stb_hexwave.h](stb_hexwave.h)** | 0.5 | audio | 680 | audio waveform synthesizer
|
||||
**[stb_image.h](stb_image.h)** | 2.30 | graphics | 7988 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
|
||||
**[stb_truetype.h](stb_truetype.h)** | 1.26 | graphics | 5079 | parse, decode, and rasterize characters from truetype fonts
|
||||
**[stb_image_write.h](stb_image_write.h)** | 1.16 | graphics | 1724 | image writing to disk: PNG, TGA, BMP
|
||||
**[stb_image_resize2.h](stb_image_resize2.h)** | 2.16 | graphics | 10650 | resize images larger/smaller with good quality
|
||||
**[stb_rect_pack.h](stb_rect_pack.h)** | 1.01 | graphics | 623 | simple 2D rectangle packer with decent quality
|
||||
**[stb_perlin.h](stb_perlin.h)** | 0.5 | graphics | 428 | perlin's revised simplex noise w/ different seeds
|
||||
**[stb_ds.h](stb_ds.h)** | 0.67 | utility | 1895 | typesafe dynamic array and hash tables for C, will compile in C++
|
||||
**[stb_sprintf.h](stb_sprintf.h)** | 1.10 | utility | 1906 | fast sprintf, snprintf for C/C++
|
||||
**[stb_textedit.h](stb_textedit.h)** | 1.14 | user interface | 1429 | guts of a text editor for games etc implementing them from scratch
|
||||
**[stb_voxel_render.h](stb_voxel_render.h)** | 0.89 | 3D graphics | 3807 | Minecraft-esque voxel rendering "engine" with many more features
|
||||
**[stb_dxt.h](stb_dxt.h)** | 1.12 | 3D graphics | 719 | Fabian "ryg" Giesen's real-time DXT compressor
|
||||
**[stb_easy_font.h](stb_easy_font.h)** | 1.1 | 3D graphics | 305 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
|
||||
**[stb_tilemap_editor.h](stb_tilemap_editor.h)** | 0.42 | game dev | 4187 | embeddable tilemap editor
|
||||
**[stb_herringbone_wa...](stb_herringbone_wang_tile.h)** | 0.7 | game dev | 1221 | herringbone Wang tile map generator
|
||||
**[stb_c_lexer.h](stb_c_lexer.h)** | 0.12 | parsing | 941 | simplify writing parsers for C-like languages
|
||||
**[stb_divide.h](stb_divide.h)** | 0.94 | math | 433 | more useful 32-bit modulus e.g. "euclidean divide"
|
||||
**[stb_connected_comp...](stb_connected_components.h)** | 0.96 | misc | 1049 | incrementally compute reachability on grids
|
||||
**[stb_leakcheck.h](stb_leakcheck.h)** | 0.6 | misc | 194 | quick-and-dirty malloc/free leak-checking
|
||||
**[stb_include.h](stb_include.h)** | 0.02 | misc | 295 | implement recursive #include support, particularly for GLSL
|
||||
|
||||
Total libraries: 21
|
||||
Total lines of C code: 51137
|
||||
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
#### What's the license?
|
||||
|
||||
These libraries are in the public domain. You can do anything you
|
||||
want with them. You have no legal obligation
|
||||
to do anything else, although I appreciate attribution.
|
||||
|
||||
They are also licensed under the MIT open source license, if you have lawyers
|
||||
who are unhappy with public domain. Every source file includes an explicit
|
||||
dual-license for you to choose from.
|
||||
|
||||
#### How do I use these libraries?
|
||||
|
||||
The idea behind single-header file libraries is that they're easy to distribute and deploy
|
||||
because all the code is contained in a single file. By default, the .h files in here act as
|
||||
their own header files, i.e. they declare the functions contained in the file but don't
|
||||
actually result in any code getting compiled.
|
||||
|
||||
So in addition, you should select _exactly one_ C/C++ source file that actually instantiates
|
||||
the code, preferably a file you're not editing frequently. This file should define a
|
||||
specific macro (this is documented per-library) to actually enable the function definitions.
|
||||
For example, to use stb_image, you should have exactly one C/C++ file that doesn't
|
||||
include stb_image.h regularly, but instead does
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
The right macro to define is pointed out right at the top of each of these libraries.
|
||||
|
||||
#### <a name="other_libs"></a> Are there other single-file public-domain/open source libraries with minimal dependencies out there?
|
||||
|
||||
[Yes.](https://github.com/nothings/single_file_libs)
|
||||
|
||||
#### If I wrap an stb library in a new library, does the new library have to be public domain/MIT?
|
||||
|
||||
No, because it's public domain you can freely relicense it to whatever license your new
|
||||
library wants to be.
|
||||
|
||||
#### What's the deal with SSE support in GCC-based compilers?
|
||||
|
||||
stb_image will either use SSE2 (if you compile with -msse2) or
|
||||
will not use any SIMD at all, rather than trying to detect the
|
||||
processor at runtime and handle it correctly. As I understand it,
|
||||
the approved path in GCC for runtime-detection require
|
||||
you to use multiple source files, one for each CPU configuration.
|
||||
Because stb_image is a header-file library that compiles in only
|
||||
one source file, there's no approved way to build both an
|
||||
SSE-enabled and a non-SSE-enabled variation.
|
||||
|
||||
While we've tried to work around it, we've had multiple issues over
|
||||
the years due to specific versions of gcc breaking what we're doing,
|
||||
so we've given up on it. See https://github.com/nothings/stb/issues/280
|
||||
and https://github.com/nothings/stb/issues/410 for examples.
|
||||
|
||||
#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow?
|
||||
|
||||
Generally they're only better in that they're easier to integrate,
|
||||
easier to use, and easier to release (single file; good API; no
|
||||
attribution requirement). They may be less featureful, slower,
|
||||
and/or use more memory. If you're already using an equivalent
|
||||
library, there's probably no good reason to switch.
|
||||
|
||||
#### Can I link directly to the table of stb libraries?
|
||||
|
||||
You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list.
|
||||
|
||||
#### Why do you list "lines of code"? It's a terrible metric.
|
||||
|
||||
Just to give you some idea of the internal complexity of the library,
|
||||
to help you manage your expectations, or to let you know what you're
|
||||
getting into. While not all the libraries are written in the same
|
||||
style, they're certainly similar styles, and so comparisons between
|
||||
the libraries are probably still meaningful.
|
||||
|
||||
Note though that the lines do include both the implementation, the
|
||||
part that corresponds to a header file, and the documentation.
|
||||
|
||||
#### Why single-file headers?
|
||||
|
||||
Windows doesn't have standard directories where libraries
|
||||
live. That makes deploying libraries in Windows a lot more
|
||||
painful than open source developers on Unix-derivates generally
|
||||
realize. (It also makes library dependencies a lot worse in Windows.)
|
||||
|
||||
There's also a common problem in Windows where a library was built
|
||||
against a different version of the runtime library, which causes
|
||||
link conflicts and confusion. Shipping the libs as headers means
|
||||
you normally just compile them straight into your project without
|
||||
making libraries, thus sidestepping that problem.
|
||||
|
||||
Making them a single file makes it very easy to just
|
||||
drop them into a project that needs them. (Of course you can
|
||||
still put them in a proper shared library tree if you want.)
|
||||
|
||||
Why not two files, one a header and one an implementation?
|
||||
The difference between 10 files and 9 files is not a big deal,
|
||||
but the difference between 2 files and 1 file is a big deal.
|
||||
You don't need to zip or tar the files up, you don't have to
|
||||
remember to attach *two* files, etc.
|
||||
|
||||
#### Why "stb"? Is this something to do with Set-Top Boxes?
|
||||
|
||||
No, they are just the initials for my name, Sean T. Barrett.
|
||||
This was not chosen out of egomania, but as a moderately sane
|
||||
way of namespacing the filenames and source function names.
|
||||
|
||||
#### Will you add more image types to stb_image.h?
|
||||
|
||||
No. As stb_image use has grown, it has become more important
|
||||
for us to focus on security of the codebase. Adding new image
|
||||
formats increases the amount of code we need to secure, so it
|
||||
is no longer worth adding new formats.
|
||||
|
||||
#### Do you have any advice on how to create my own single-file library?
|
||||
|
||||
Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
|
||||
|
||||
#### Why public domain?
|
||||
|
||||
I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons.
|
||||
Some of them are listed here:
|
||||
https://github.com/nothings/stb/blob/master/docs/why_public_domain.md
|
||||
|
||||
#### Why C?
|
||||
|
||||
Primarily, because I use C, not C++. But it does also make it easier
|
||||
for other people to use them from other languages.
|
||||
|
||||
#### Why not C99? stdint.h, declare-anywhere, etc.
|
||||
|
||||
I still use MSVC 6 (1998) as my IDE because it has better human factors
|
||||
for me than later versions of MSVC.
|
||||
2
thirdparty/stb/SECURITY.md
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Security Policy
|
||||
This project discusses security-relevant bugs in public in Github Issues and Pull Requests, and it may take significant time for security fixes to be implemented or merged. If this poses an unreasonable risk to your project, do not use stb libraries.
|
||||
BIN
thirdparty/stb/data/atari_8bit_font_revised.png
vendored
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
thirdparty/stb/data/easy_font_raw.png
vendored
Normal file
|
After Width: | Height: | Size: 645 B |
4
thirdparty/stb/data/herringbone/license.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
All files in this directory are in the public domain. Where
|
||||
a public domain declaration is not recognized, you are granted
|
||||
a license to freely use, modify, and redistribute them in
|
||||
any way you choose.
|
||||
BIN
thirdparty/stb/data/herringbone/template_caves_limit_connectivity.png
vendored
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
thirdparty/stb/data/herringbone/template_caves_tiny_corridors.png
vendored
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
thirdparty/stb/data/herringbone/template_corner_caves.png
vendored
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
thirdparty/stb/data/herringbone/template_horizontal_corridors_v1.png
vendored
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
thirdparty/stb/data/herringbone/template_horizontal_corridors_v2.png
vendored
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
thirdparty/stb/data/herringbone/template_horizontal_corridors_v3.png
vendored
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
thirdparty/stb/data/herringbone/template_limit_connectivity_fat.png
vendored
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
thirdparty/stb/data/herringbone/template_limited_connectivity.png
vendored
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
thirdparty/stb/data/herringbone/template_maze_2_wide.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
thirdparty/stb/data/herringbone/template_maze_plus_2_wide.png
vendored
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
thirdparty/stb/data/herringbone/template_open_areas.png
vendored
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
thirdparty/stb/data/herringbone/template_ref2_corner_caves.png
vendored
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
thirdparty/stb/data/herringbone/template_rooms_and_corridors.png
vendored
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
thirdparty/stb/data/herringbone/template_rooms_and_corridors_2_wide_diagonal_bias.png
vendored
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
thirdparty/stb/data/herringbone/template_rooms_limit_connectivity.png
vendored
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
thirdparty/stb/data/herringbone/template_round_rooms_diagonal_corridors.png
vendored
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
thirdparty/stb/data/herringbone/template_sean_dungeon.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
thirdparty/stb/data/herringbone/template_simple_caves_2_wide.png
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
thirdparty/stb/data/herringbone/template_square_rooms_with_random_rects.png
vendored
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
thirdparty/stb/data/map_01.png
vendored
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
thirdparty/stb/data/map_02.png
vendored
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
thirdparty/stb/data/map_03.png
vendored
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
1055
thirdparty/stb/deprecated/rrsprintf.h
vendored
Normal file
13111
thirdparty/stb/deprecated/stb.h
vendored
Normal file
4678
thirdparty/stb/deprecated/stb_image.c
vendored
Normal file
2634
thirdparty/stb/deprecated/stb_image_resize.h
vendored
Normal file
29
thirdparty/stb/deprecated/stretch_test.c
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// check that stb_truetype compiles with no stb_rect_pack.h
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "stb_truetype.h"
|
||||
|
||||
#define STB_DS_IMPLEMENTATION
|
||||
#include "stb_ds.h"
|
||||
#include <assert.h>
|
||||
|
||||
int main(int arg, char **argv)
|
||||
{
|
||||
int i;
|
||||
int *arr = NULL;
|
||||
|
||||
for (i=0; i < 1000000; ++i)
|
||||
arrput(arr, i);
|
||||
|
||||
assert(arrlen(arr) == 1000000);
|
||||
for (i=0; i < 1000000; ++i)
|
||||
assert(arr[i] == i);
|
||||
|
||||
arrfree(arr);
|
||||
arr = NULL;
|
||||
|
||||
for (i=0; i < 1000; ++i)
|
||||
arrput(arr, 1000);
|
||||
assert(arrlen(arr) == 1000000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
263
thirdparty/stb/deprecated/stretchy_buffer.h
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
// stretchy_buffer.h - v1.04 - public domain - nothings.org/stb
|
||||
// a vector<>-like dynamic array for C
|
||||
//
|
||||
// version history:
|
||||
// 1.04 - fix warning
|
||||
// 1.03 - compile as C++ maybe
|
||||
// 1.02 - tweaks to syntax for no good reason
|
||||
// 1.01 - added a "common uses" documentation section
|
||||
// 1.0 - fixed bug in the version I posted prematurely
|
||||
// 0.9 - rewrite to try to avoid strict-aliasing optimization
|
||||
// issues, but won't compile as C++
|
||||
//
|
||||
// Will probably not work correctly with strict-aliasing optimizations.
|
||||
//
|
||||
// The idea:
|
||||
//
|
||||
// This implements an approximation to C++ vector<> for C, in that it
|
||||
// provides a generic definition for dynamic arrays which you can
|
||||
// still access in a typesafe way using arr[i] or *(arr+i). However,
|
||||
// it is simply a convenience wrapper around the common idiom of
|
||||
// of keeping a set of variables (in a struct or globals) which store
|
||||
// - pointer to array
|
||||
// - the length of the "in-use" part of the array
|
||||
// - the current size of the allocated array
|
||||
//
|
||||
// I find it to be the single most useful non-built-in-structure when
|
||||
// programming in C (hash tables a close second), but to be clear
|
||||
// it lacks many of the capabilities of C++ vector<>: there is no
|
||||
// range checking, the object address isn't stable (see next section
|
||||
// for details), the set of methods available is small (although
|
||||
// the file stb.h has another implementation of stretchy buffers
|
||||
// called 'stb_arr' which provides more methods, e.g. for insertion
|
||||
// and deletion).
|
||||
//
|
||||
// How to use:
|
||||
//
|
||||
// Unlike other stb header file libraries, there is no need to
|
||||
// define an _IMPLEMENTATION symbol. Every #include creates as
|
||||
// much implementation is needed.
|
||||
//
|
||||
// stretchy_buffer.h does not define any types, so you do not
|
||||
// need to #include it to before defining data types that are
|
||||
// stretchy buffers, only in files that *manipulate* stretchy
|
||||
// buffers.
|
||||
//
|
||||
// If you want a stretchy buffer aka dynamic array containing
|
||||
// objects of TYPE, declare such an array as:
|
||||
//
|
||||
// TYPE *myarray = NULL;
|
||||
//
|
||||
// (There is no typesafe way to distinguish between stretchy
|
||||
// buffers and regular arrays/pointers; this is necessary to
|
||||
// make ordinary array indexing work on these objects.)
|
||||
//
|
||||
// Unlike C++ vector<>, the stretchy_buffer has the same
|
||||
// semantics as an object that you manually malloc and realloc.
|
||||
// The pointer may relocate every time you add a new object
|
||||
// to it, so you:
|
||||
//
|
||||
// 1. can't take long-term pointers to elements of the array
|
||||
// 2. have to return the pointer from functions which might expand it
|
||||
// (either as a return value or by storing it to a ptr-to-ptr)
|
||||
//
|
||||
// Now you can do the following things with this array:
|
||||
//
|
||||
// sb_free(TYPE *a) free the array
|
||||
// sb_count(TYPE *a) the number of elements in the array
|
||||
// sb_push(TYPE *a, TYPE v) adds v on the end of the array, a la push_back
|
||||
// sb_add(TYPE *a, int n) adds n uninitialized elements at end of array & returns pointer to first added
|
||||
// sb_last(TYPE *a) returns an lvalue of the last item in the array
|
||||
// a[n] access the nth (counting from 0) element of the array
|
||||
//
|
||||
// #define STRETCHY_BUFFER_NO_SHORT_NAMES to only export
|
||||
// names of the form 'stb_sb_' if you have a name that would
|
||||
// otherwise collide.
|
||||
//
|
||||
// Note that these are all macros and many of them evaluate
|
||||
// their arguments more than once, so the arguments should
|
||||
// be side-effect-free.
|
||||
//
|
||||
// Note that 'TYPE *a' in sb_push and sb_add must be lvalues
|
||||
// so that the library can overwrite the existing pointer if
|
||||
// the object has to be reallocated.
|
||||
//
|
||||
// In an out-of-memory condition, the code will try to
|
||||
// set up a null-pointer or otherwise-invalid-pointer
|
||||
// exception to happen later. It's possible optimizing
|
||||
// compilers could detect this write-to-null statically
|
||||
// and optimize away some of the code, but it should only
|
||||
// be along the failure path. Nevertheless, for more security
|
||||
// in the face of such compilers, #define STRETCHY_BUFFER_OUT_OF_MEMORY
|
||||
// to a statement such as assert(0) or exit(1) or something
|
||||
// to force a failure when out-of-memory occurs.
|
||||
//
|
||||
// Common use:
|
||||
//
|
||||
// The main application for this is when building a list of
|
||||
// things with an unknown quantity, either due to loading from
|
||||
// a file or through a process which produces an unpredictable
|
||||
// number.
|
||||
//
|
||||
// My most common idiom is something like:
|
||||
//
|
||||
// SomeStruct *arr = NULL;
|
||||
// while (something)
|
||||
// {
|
||||
// SomeStruct new_one;
|
||||
// new_one.whatever = whatever;
|
||||
// new_one.whatup = whatup;
|
||||
// new_one.foobar = barfoo;
|
||||
// sb_push(arr, new_one);
|
||||
// }
|
||||
//
|
||||
// and various closely-related factorings of that. For example,
|
||||
// you might have several functions to create/init new SomeStructs,
|
||||
// and if you use the above idiom, you might prefer to make them
|
||||
// return structs rather than take non-const-pointers-to-structs,
|
||||
// so you can do things like:
|
||||
//
|
||||
// SomeStruct *arr = NULL;
|
||||
// while (something)
|
||||
// {
|
||||
// if (case_A) {
|
||||
// sb_push(arr, some_func1());
|
||||
// } else if (case_B) {
|
||||
// sb_push(arr, some_func2());
|
||||
// } else {
|
||||
// sb_push(arr, some_func3());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Note that the above relies on the fact that sb_push doesn't
|
||||
// evaluate its second argument more than once. The macros do
|
||||
// evaluate the *array* argument multiple times, and numeric
|
||||
// arguments may be evaluated multiple times, but you can rely
|
||||
// on the second argument of sb_push being evaluated only once.
|
||||
//
|
||||
// Of course, you don't have to store bare objects in the array;
|
||||
// if you need the objects to have stable pointers, store an array
|
||||
// of pointers instead:
|
||||
//
|
||||
// SomeStruct **arr = NULL;
|
||||
// while (something)
|
||||
// {
|
||||
// SomeStruct *new_one = malloc(sizeof(*new_one));
|
||||
// new_one->whatever = whatever;
|
||||
// new_one->whatup = whatup;
|
||||
// new_one->foobar = barfoo;
|
||||
// sb_push(arr, new_one);
|
||||
// }
|
||||
//
|
||||
// How it works:
|
||||
//
|
||||
// A long-standing tradition in things like malloc implementations
|
||||
// is to store extra data before the beginning of the block returned
|
||||
// to the user. The stretchy buffer implementation here uses the
|
||||
// same trick; the current-count and current-allocation-size are
|
||||
// stored before the beginning of the array returned to the user.
|
||||
// (This means you can't directly free() the pointer, because the
|
||||
// allocated pointer is different from the type-safe pointer provided
|
||||
// to the user.)
|
||||
//
|
||||
// The details are trivial and implementation is straightforward;
|
||||
// the main trick is in realizing in the first place that it's
|
||||
// possible to do this in a generic, type-safe way in C.
|
||||
//
|
||||
// Contributors:
|
||||
//
|
||||
// Timothy Wright (github:ZenToad)
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
#ifndef STB_STRETCHY_BUFFER_H_INCLUDED
|
||||
#define STB_STRETCHY_BUFFER_H_INCLUDED
|
||||
|
||||
#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES
|
||||
#define sb_free stb_sb_free
|
||||
#define sb_push stb_sb_push
|
||||
#define sb_count stb_sb_count
|
||||
#define sb_add stb_sb_add
|
||||
#define sb_last stb_sb_last
|
||||
#endif
|
||||
|
||||
#define stb_sb_free(a) ((a) ? free(stb__sbraw(a)),0 : 0)
|
||||
#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
|
||||
#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0)
|
||||
#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
|
||||
#define stb_sb_last(a) ((a)[stb__sbn(a)-1])
|
||||
|
||||
#define stb__sbraw(a) ((int *) (void *) (a) - 2)
|
||||
#define stb__sbm(a) stb__sbraw(a)[0]
|
||||
#define stb__sbn(a) stb__sbraw(a)[1]
|
||||
|
||||
#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a))
|
||||
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
|
||||
#define stb__sbgrow(a,n) (*((void **)&(a)) = stb__sbgrowf((a), (n), sizeof(*(a))))
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static void * stb__sbgrowf(void *arr, int increment, int itemsize)
|
||||
{
|
||||
int dbl_cur = arr ? 2*stb__sbm(arr) : 0;
|
||||
int min_needed = stb_sb_count(arr) + increment;
|
||||
int m = dbl_cur > min_needed ? dbl_cur : min_needed;
|
||||
int *p = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
|
||||
if (p) {
|
||||
if (!arr)
|
||||
p[1] = 0;
|
||||
p[0] = m;
|
||||
return p+2;
|
||||
} else {
|
||||
#ifdef STRETCHY_BUFFER_OUT_OF_MEMORY
|
||||
STRETCHY_BUFFER_OUT_OF_MEMORY ;
|
||||
#endif
|
||||
return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later
|
||||
}
|
||||
}
|
||||
#endif // STB_STRETCHY_BUFFER_H_INCLUDED
|
||||
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
28
thirdparty/stb/deprecated/stretchy_buffer.txt
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// stretchy buffer // init: NULL // free: sbfree() // push_back: sbpush() // size: sbcount() //
|
||||
#define sbfree(a) ((a) ? free(stb__sbraw(a)),0 : 0)
|
||||
#define sbpush(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
|
||||
#define sbcount(a) ((a) ? stb__sbn(a) : 0)
|
||||
#define sbadd(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
|
||||
#define sblast(a) ((a)[stb__sbn(a)-1])
|
||||
|
||||
#include <stdlib.h>
|
||||
#define stb__sbraw(a) ((int *) (a) - 2)
|
||||
#define stb__sbm(a) stb__sbraw(a)[0]
|
||||
#define stb__sbn(a) stb__sbraw(a)[1]
|
||||
|
||||
#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+n >= stb__sbm(a))
|
||||
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
|
||||
#define stb__sbgrow(a,n) stb__sbgrowf((void **) &(a), (n), sizeof(*(a)))
|
||||
|
||||
static void stb__sbgrowf(void **arr, int increment, int itemsize)
|
||||
{
|
||||
int m = *arr ? 2*stb__sbm(*arr)+increment : increment+1;
|
||||
void *p = realloc(*arr ? stb__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
|
||||
assert(p);
|
||||
if (p) {
|
||||
if (!*arr) ((int *) p)[1] = 0;
|
||||
*arr = (void *) ((int *) p + 2);
|
||||
stb__sbm(*arr) = m;
|
||||
}
|
||||
}
|
||||
|
||||
1
thirdparty/stb/docs/other_libs.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Moved to https://github.com/nothings/single_file_libs
|
||||
185
thirdparty/stb/docs/stb_howto.txt
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
Lessons learned about how to make a header-file library
|
||||
V1.0
|
||||
September 2013 Sean Barrett
|
||||
|
||||
Things to do in an stb-style header-file library,
|
||||
and rationales:
|
||||
|
||||
|
||||
1. #define LIBRARYNAME_IMPLEMENTATION
|
||||
|
||||
Use a symbol like the above to control creating
|
||||
the implementation. (I used a far-less-clear name
|
||||
in my first header-file library; it became
|
||||
clear that was a mistake once I had multiple
|
||||
libraries.)
|
||||
|
||||
Include a "header-file" section with header-file
|
||||
guards and declarations for all the functions,
|
||||
but only guard the implementation with LIBRARYNAME_IMPLEMENTATION,
|
||||
not the header-file guard. That way, if client's
|
||||
header file X includes your header file for
|
||||
declarations, they can still include header file X
|
||||
in the source file that creates the implementation;
|
||||
if you guard the implementation too, then the first
|
||||
include (before the #define) creates the declarations,
|
||||
and the second one (after the #define) does nothing.
|
||||
|
||||
|
||||
2. AVOID DEPENDENCIES
|
||||
|
||||
Don't rely on anything other than the C standard libraries.
|
||||
|
||||
(If you're creating a library specifically to leverage/wrap
|
||||
some other library, then obviously you can rely on that
|
||||
library. But if that library is public domain, you might
|
||||
be better off directly embedding the source, to reduce
|
||||
dependencies for your clients. But of course now you have
|
||||
to update whenever that library updates.)
|
||||
|
||||
If you use stdlib, consider wrapping all stdlib calls in
|
||||
macros, and then conditionally define those macros to the
|
||||
stdlib function, allowing the user to replace them.
|
||||
|
||||
For functions with side effects, like memory allocations,
|
||||
consider letting the user pass in a context and pass
|
||||
that in to the macros. (The stdlib versions will ignore
|
||||
the parameter.) Otherwise, users may have to use global
|
||||
or thread-local variables to achieve the same effect.
|
||||
|
||||
|
||||
3. AVOID MALLOC
|
||||
|
||||
You can't always do this, but when you can, embedded developers
|
||||
will appreciate it. I almost never bother avoiding, as it's
|
||||
too much work (and in some cases is pretty infeasible;
|
||||
see http://nothings.org/gamedev/font_rendering_malloc.txt ).
|
||||
But it's definitely something one of the things I've gotten
|
||||
the most pushback on from potential users.
|
||||
|
||||
|
||||
4. ALLOW STATIC IMPLEMENTATION
|
||||
|
||||
Have a #define which makes function declarations and
|
||||
function definitions static. This makes the implementation
|
||||
private to the source file that creates it. This allows
|
||||
people to use your library multiple times in their project
|
||||
without collision. (This is only necessary if your library
|
||||
has configuration macros or global state, or if your
|
||||
library has multiple versions that are not backwards
|
||||
compatible. I've run into both of those cases.)
|
||||
|
||||
|
||||
5. MAKE ACCESSIBLE FROM C
|
||||
|
||||
Making your code accessible from C instead of C++ (i.e.
|
||||
either coding in C, or using extern "C") makes it more
|
||||
straightforward to be used in C and in other languages,
|
||||
which often only have support for C bindings, not C++.
|
||||
(One of the earliest results I found in googling for
|
||||
stb_image was a Haskell wrapper.) Otherwise, people
|
||||
have to wrap it in another set of function calls, and
|
||||
the whole point here is to make it convenient for people
|
||||
to use, isn't it? (See below.)
|
||||
|
||||
I prefer to code entirely in C, so the source file that
|
||||
instantiates the implementation can be C itself, for
|
||||
those crazy people out there who are programming in C.
|
||||
But it's probably not a big hardship for a C programmer
|
||||
to create a single C++ source file to instantiate your
|
||||
library.
|
||||
|
||||
|
||||
6. NAMESPACE PRIVATE FUNCTIONS
|
||||
|
||||
Try to avoid having names in your source code that
|
||||
will cause conflicts with identical names in client
|
||||
code. You can do this either by namespacing in C++,
|
||||
or prefixing with your library name in C.
|
||||
|
||||
In C, generally, I use the same prefix for API
|
||||
functions and private symbols, such as "stbtt_"
|
||||
for stb_truetype; but private functions (and
|
||||
static globals) use a second underscore as
|
||||
in "stbtt__" to further minimize the chance of
|
||||
additional collisions in the unlikely but not
|
||||
impossible event that users write wrapper
|
||||
functions that have names of the form "stbtt_".
|
||||
(Consider the user that has used "stbtt_foo"
|
||||
*successfully*, and then upgrades to a new
|
||||
version of your library which has a new private
|
||||
function named either "stbtt_foo" or "stbtt__foo".)
|
||||
|
||||
Note that the double-underscore is reserved for
|
||||
use by the compiler, but (1) there is nothing
|
||||
reserved for "middleware", i.e. libraries
|
||||
desiring to avoid conflicts with user symbols
|
||||
have no other good options, and (2) in practice
|
||||
no compilers use double-underscore in the middle
|
||||
rather than the beginning/end. (Unfortunately,
|
||||
there is at least one videogame-console compiler that
|
||||
will warn about double-underscores by default.)
|
||||
|
||||
|
||||
7. EASY-TO-COMPLY LICENSE
|
||||
|
||||
I make my libraries public domain. You don't have to.
|
||||
But my goal in releasing stb-style libraries is to
|
||||
reduce friction for potential users as much as
|
||||
possible. That means:
|
||||
|
||||
a. easy to build (what this file is mostly about)
|
||||
b. easy to invoke (which requires good API design)
|
||||
c. easy to deploy (which is about licensing)
|
||||
|
||||
I choose to place all my libraries in the public
|
||||
domain, abjuring copyright, rather than license
|
||||
the libraries. This has some benefits and some
|
||||
drawbacks.
|
||||
|
||||
Any license which is "viral" to modifications
|
||||
causes worries for lawyers, even if their programmers
|
||||
aren't modifying it.
|
||||
|
||||
Any license which requires crediting in documentation
|
||||
adds friction which can add up. Valve used to have
|
||||
a page with a list of all of these on their web site,
|
||||
and it was insane, and obviously nobody ever looked
|
||||
at it so why would you care whether your credit appeared
|
||||
there?
|
||||
|
||||
Permissive licenses like zlib and BSD license are
|
||||
perfectly reasonable, but they are very wordy and
|
||||
have only two benefits over public domain: legally-mandated
|
||||
attribution and liability-control. I do not believe these
|
||||
are worth the excessive verbosity and user-unfriendliness
|
||||
these licenses induce, especially in the single-file
|
||||
case where those licenses tend to be at the top of
|
||||
the file, the first thing you see. (To the specific
|
||||
points, I have had no trouble receiving attribution
|
||||
for my libraries; liability in the face of no explicit
|
||||
disclaimer of liability is an open question.)
|
||||
|
||||
However, public domain has frictions of its own, because
|
||||
public domain declarations aren't necessary recognized
|
||||
in the USA and some other locations. For that reason,
|
||||
I recommend a declaration along these lines:
|
||||
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
|
||||
I typically place this declaration at the end of the initial
|
||||
comment block of the file and just say 'public domain'
|
||||
at the top.
|
||||
|
||||
I have had people say they couldn't use one of my
|
||||
libraries because it was only "public domain" and didn't
|
||||
have the additional fallback clause, who asked if
|
||||
I could dual-license it under a traditional license.
|
||||
|
||||
My answer: they can create a derivative work by
|
||||
modifying one character, and then license that however
|
||||
they like. (Indeed, *adding* the zlib or BSD license
|
||||
would be such a modification!) Unfortunately, their
|
||||
lawyers reportedly didn't like that answer. :(
|
||||
173
thirdparty/stb/docs/stb_voxel_render_interview.md
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
# An interview with STB about stb_voxel_render.h
|
||||
|
||||
**Q:**
|
||||
I suppose you really like Minecraft?
|
||||
|
||||
**A:**
|
||||
Not really. I mean, I do own it and play it some, and
|
||||
I do watch YouTube videos of other people playing it
|
||||
once in a while, but I'm not saying it's that great.
|
||||
|
||||
But I do love voxels. I've been playing with voxel rendering
|
||||
since the mid-late 90's when we were still doing software
|
||||
rendering and thinking maybe polygons weren't the answer.
|
||||
Once GPUs came along that kind of died off, at least until
|
||||
Minecraft brought it back to attention.
|
||||
|
||||
**Q:**
|
||||
Do you expect people will make a lot of Minecraft clones
|
||||
with this?
|
||||
|
||||
**A:**
|
||||
I hope not!
|
||||
|
||||
For one thing, it's a terrible idea for the
|
||||
developer. Remember before Minecraft was on the Xbox 360,
|
||||
there were a ton of "indie" clones (some maybe making
|
||||
decent money even), but then the real Minecraft came out
|
||||
and just crushed them (as far as I know). It's just not
|
||||
something you really want to compete with.
|
||||
|
||||
The reason I made this library is because I'd like
|
||||
to see more games with Minecraft's *art style*, not
|
||||
necessary its *gameplay*.
|
||||
|
||||
I can understand the urge to clone the gameplay. When
|
||||
you have a world made of voxels/blocks, there are a
|
||||
few things that become incredibly easy to do that would
|
||||
otherwise be very hard (at least for an indie) to do in 3D.
|
||||
One thing is that procedural generation becomes much easier.
|
||||
Another is that destructible environments are easy. Another
|
||||
is that you have a world where your average user can build
|
||||
stuff that they find satisfactory.
|
||||
|
||||
Minecraft is at a sort of local maximum, a sweet spot, where
|
||||
it leverages all of those easy-to-dos. And so I'm sure it's
|
||||
hard to look at the space of 'games using voxels' and move
|
||||
away from that local maximum, to give up some of that.
|
||||
But I think that's what people should do.
|
||||
|
||||
**Q:**
|
||||
So what else can people do with stb_voxel_render?
|
||||
|
||||
**A:**
|
||||
All of those benefits I mentioned above are still valid even
|
||||
if you stay away from the sweet spot. You can make a 3D roguelike
|
||||
without player-creation/destruction that uses procedural generation.
|
||||
You could make a shooter with pre-designed maps but destructible
|
||||
environments.
|
||||
|
||||
And I'm sure there are other possible benefits to using voxels/blocks.
|
||||
Hopefully this will make it easier for people to explore the space.
|
||||
|
||||
The library has a pretty wide range of features to allow
|
||||
people to come up with some distinctive looks. For example,
|
||||
the art style of Continue?9876543210 was one of the inspirations
|
||||
for trying to make the multitexturing capabilities flexible.
|
||||
I'm terrible at art, so this isn't really something I can
|
||||
come up with myself, but I tried to put in flexible
|
||||
technology that could be used multiple ways.
|
||||
|
||||
One thing I did intentionally was try to make it possible to
|
||||
make nicer looking ground terrain, using the half-height
|
||||
slopes and "weird slopes". There are Minecraft mods with
|
||||
drivable cars and they just go up these blocky slopes and,
|
||||
like, what? So I wanted you to be able to make smoother
|
||||
terrain, either just for the look, or for vehicles etc.
|
||||
Also, you can spatially cross-fade between two ground textures for
|
||||
that classic bad dirt/grass transition that has shipped
|
||||
in plenty of professional games. Of course, you could
|
||||
just use a separate non-voxel ground renderer for all of
|
||||
this. But this way, you can seamlessly integrate everything
|
||||
else with it. E.g. in your authoring tool (or procedural
|
||||
generation) you can make smooth ground and then cut a
|
||||
sharp-edged hole in it for a building's basement or whatever.
|
||||
|
||||
Another thing you can do is work at a very different scale.
|
||||
In Minecraft, a person is just under 2 blocks tall. In
|
||||
Ace of Spades, a person is just under 3 blocks tall. Why
|
||||
not 4 or 6? Well, partly because you just need a lot more
|
||||
voxels; if a meter is 2 voxels in Mineraft and 4 voxels in
|
||||
your game, and you draw the same number of voxels due to
|
||||
hardware limits, then your game has half the view distance
|
||||
of Minecraft. Since stb_voxel_render is designed to keep
|
||||
the meshes small and render efficiently, you can push the
|
||||
view distance out further than Minecraft--or use a similar
|
||||
view distance and a higher voxel resolution. You could also
|
||||
stop making infinite worlds and work at entirely different
|
||||
scales; where Minecraft is 1 voxel per meter, you could
|
||||
have 20 voxels per meter and make a small arena that's
|
||||
50 meters wide and 5 meters tall.
|
||||
|
||||
Back when the voxel game Voxatron was announced, the weekend
|
||||
after the trailer came out I wrote my own little GPU-accelerated
|
||||
version of the engine and thought that was pretty cool. I've
|
||||
been tempted many times to extract that and release it
|
||||
as a library, but
|
||||
I don't want to steal Voxatron's thunder so I've avoided
|
||||
it. You could use this engine to do the same kind of thing,
|
||||
although it won't be as efficient as an engine dedicated to
|
||||
that style of thing would be.
|
||||
|
||||
**Q:**
|
||||
What one thing would you really like to see somebody do?
|
||||
|
||||
**A:**
|
||||
Before Unity, 3D has seemed deeply problematic in the indie
|
||||
space. Software like GameMaker has tried to support 3D but
|
||||
it seems like little of note has been done with it.
|
||||
|
||||
Minecraft has shown that people can build worlds with the
|
||||
Minecraft toolset far more easily than we've ever seen from those
|
||||
other tools. Obviously people have done great things with
|
||||
Unity, but those people are much closer to professional
|
||||
developers; typically they still need real 3D modelling
|
||||
and all of that stuff.
|
||||
|
||||
So what I'd really like to see is someone build some kind
|
||||
of voxel-game-construction-set. Start with stb_voxel_render,
|
||||
maybe expose all the flexibility of stb_voxel_render (so
|
||||
people can do different things). Thrown in lua or something
|
||||
else for scripting, make some kind of editor that feels
|
||||
at least as good as Minecraft and Infinifactory, and see
|
||||
where that gets you.
|
||||
|
||||
**Q:**
|
||||
Why'd you make this library?
|
||||
|
||||
**A:**
|
||||
Mainly as a way of releasing this technology I've been working
|
||||
on since 2011 and seemed unlikely to ever ship myself. In 2011
|
||||
I was playing the voxel shooter Ace of Spades. One of the maps
|
||||
that we played on was a partial port of Broville (which is the
|
||||
first Minecraft map in stb_voxel_render release trailer). I'd
|
||||
made a bunch of procedural level generators for the game, and
|
||||
I started trying to make a city generator inspired by Broville.
|
||||
|
||||
But I realized it would be a lot of work, and of very little
|
||||
value (most of my maps didn't get much play because people
|
||||
preferred to play on maps where they could charge straight
|
||||
at the enemies and shoot them as fast as possible). So I
|
||||
wrote my own voxel engine and started working on a procedural
|
||||
city game. But I got bogged down after I finally got the road
|
||||
generator working and never got anywhere with building
|
||||
generation or gameplay.
|
||||
|
||||
stb_voxel_render is actually a complete rewrite from scratch,
|
||||
but it's based a lot on what I learned from that previous work.
|
||||
|
||||
**Q:**
|
||||
About the release video... how long did that take to edit?
|
||||
|
||||
**A:**
|
||||
About seven or eight hours. I had the first version done in
|
||||
maybe six or seven hours, but then I realized I'd left out
|
||||
one clip, and when I went back to add it I also gussied up
|
||||
a couple other moments in the video. But there was something
|
||||
basically identical to it that was done in around six.
|
||||
|
||||
**Q:**
|
||||
Ok, that's it. Thanks, me.
|
||||
|
||||
**A:**
|
||||
Thanks *me!*
|
||||
117
thirdparty/stb/docs/why_public_domain.md
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
My collected rationales for placing these libraries
|
||||
in the public domain:
|
||||
|
||||
1. Public domain vs. viral licenses
|
||||
|
||||
Why is this library public domain?
|
||||
Because more people will use it. Because it's not viral, people are
|
||||
not obligated to give back, so you could argue that it hurts the
|
||||
development of it, and then because it doesn't develop as well it's
|
||||
not as good, and then because it's not as good, in the long run
|
||||
maybe fewer people will use it. I have total respect for that
|
||||
opinion, but I just don't believe it myself for most software.
|
||||
|
||||
2. Public domain vs. attribution-required licenses
|
||||
|
||||
The primary difference between public domain and, say, a Creative Commons
|
||||
commercial / non-share-alike / attribution license is solely the
|
||||
requirement for attribution. (Similarly the BSD license and such.)
|
||||
While I would *appreciate* acknowledgement and attribution, I believe
|
||||
that it is foolish to place a legal encumberment (i.e. a license) on
|
||||
the software *solely* to get attribution.
|
||||
|
||||
In other words, I'm arguing that PD is superior to the BSD license and
|
||||
the Creative Commons 'Attribution' license. If the license offers
|
||||
anything besides attribution -- as does, e.g., CC NonCommercial-ShareAlike,
|
||||
or the GPL -- that's a separate discussion.
|
||||
|
||||
3. Other aspects of BSD-style licenses besides attribution
|
||||
|
||||
Permissive licenses like zlib and BSD license are perfectly reasonable
|
||||
in their requirements, but they are very wordy and
|
||||
have only two benefits over public domain: legally-mandated
|
||||
attribution and liability-control. I do not believe these
|
||||
are worth the excessive verbosity and user-unfriendliness
|
||||
these licenses induce, especially in the single-file
|
||||
case where those licenses tend to be at the top of
|
||||
the file, the first thing you see.
|
||||
|
||||
To the specific points, I have had no trouble receiving
|
||||
attribution for my libraries; liability in the face of
|
||||
no explicit disclaimer of liability is an open question,
|
||||
but one I have a lot of difficulty imagining there being
|
||||
any actual doubt about in court. Sometimes I explicitly
|
||||
note in my libraries that I make no guarantees about them
|
||||
being fit for purpose, but it's pretty absurd to do this;
|
||||
as a whole, it comes across as "here is a library to decode
|
||||
vorbis audio files, but it may not actually work and if
|
||||
you have problems it's not my fault, but also please
|
||||
report bugs so I can fix them"--so dumb!
|
||||
|
||||
4. full discussion from stb_howto.txt on what YOU should do for YOUR libs
|
||||
|
||||
```
|
||||
EASY-TO-COMPLY LICENSE
|
||||
|
||||
I make my libraries public domain. You don't have to.
|
||||
But my goal in releasing stb-style libraries is to
|
||||
reduce friction for potential users as much as
|
||||
possible. That means:
|
||||
|
||||
a. easy to build (what this file is mostly about)
|
||||
b. easy to invoke (which requires good API design)
|
||||
c. easy to deploy (which is about licensing)
|
||||
|
||||
I choose to place all my libraries in the public
|
||||
domain, abjuring copyright, rather than license
|
||||
the libraries. This has some benefits and some
|
||||
drawbacks.
|
||||
|
||||
Any license which is "viral" to modifications
|
||||
causes worries for lawyers, even if their programmers
|
||||
aren't modifying it.
|
||||
|
||||
Any license which requires crediting in documentation
|
||||
adds friction which can add up. Valve has a huge list
|
||||
(http://nothings.org/remote/ThirdPartyLegalNotices_steam_2019.html)
|
||||
of all of these included in each game they ship,
|
||||
and it's insane, and obviously nobody ever looks
|
||||
at it so why would you care whether your credit
|
||||
appeared there?
|
||||
|
||||
Permissive licenses like zlib and BSD license are
|
||||
perfectly reasonable, but they are very wordy and
|
||||
have only two benefits over public domain: legally-mandated
|
||||
attribution and liability-control. I do not believe these
|
||||
are worth the excessive verbosity and user-unfriendliness
|
||||
these licenses induce, especially in the single-file
|
||||
case where those licenses tend to be at the top of
|
||||
the file, the first thing you see. (To the specific
|
||||
points, I have had no trouble receiving attribution
|
||||
for my libraries; liability in the face of no explicit
|
||||
disclaimer of liability is an open question.)
|
||||
|
||||
However, public domain has frictions of its own, because
|
||||
public domain declarations aren't necessary recognized
|
||||
in the USA and some other locations. For that reason,
|
||||
I recommend a declaration along these lines:
|
||||
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
|
||||
I typically place this declaration at the end of the initial
|
||||
comment block of the file and just say 'public domain'
|
||||
at the top.
|
||||
|
||||
I have had people say they couldn't use one of my
|
||||
libraries because it was only "public domain" and didn't
|
||||
have the additional fallback clause, who asked if
|
||||
I could dual-license it under a traditional license.
|
||||
|
||||
My answer: they can create a derivative work by
|
||||
modifying one character, and then license that however
|
||||
they like. (Indeed, *adding* the zlib or BSD license
|
||||
would be such a modification!) Unfortunately, their
|
||||
lawyers reportedly didn't like that answer. :(
|
||||
```
|
||||
941
thirdparty/stb/stb_c_lexer.h
vendored
Normal file
@@ -0,0 +1,941 @@
|
||||
// stb_c_lexer.h - v0.12 - public domain Sean Barrett 2013
|
||||
// lexer for making little C-like languages with recursive-descent parsers
|
||||
//
|
||||
// This file provides both the interface and the implementation.
|
||||
// To instantiate the implementation,
|
||||
// #define STB_C_LEXER_IMPLEMENTATION
|
||||
// in *ONE* source file, before #including this file.
|
||||
//
|
||||
// The default configuration is fairly close to a C lexer, although
|
||||
// suffixes on integer constants are not handled (you can override this).
|
||||
//
|
||||
// History:
|
||||
// 0.12 fix compilation bug for NUL support; better support separate inclusion
|
||||
// 0.11 fix clang static analysis warning
|
||||
// 0.10 fix warnings
|
||||
// 0.09 hex floats, no-stdlib fixes
|
||||
// 0.08 fix bad pointer comparison
|
||||
// 0.07 fix mishandling of hexadecimal constants parsed by strtol
|
||||
// 0.06 fix missing next character after ending quote mark (Andreas Fredriksson)
|
||||
// 0.05 refixed get_location because github version had lost the fix
|
||||
// 0.04 fix octal parsing bug
|
||||
// 0.03 added STB_C_LEX_DISCARD_PREPROCESSOR option
|
||||
// refactor API to simplify (only one struct instead of two)
|
||||
// change literal enum names to have 'lit' at the end
|
||||
// 0.02 first public release
|
||||
//
|
||||
// Status:
|
||||
// - haven't tested compiling as C++
|
||||
// - haven't tested the float parsing path
|
||||
// - haven't tested the non-default-config paths (e.g. non-stdlib)
|
||||
// - only tested default-config paths by eyeballing output of self-parse
|
||||
//
|
||||
// - haven't implemented multiline strings
|
||||
// - haven't implemented octal/hex character constants
|
||||
// - haven't implemented support for unicode CLEX_char
|
||||
// - need to expand error reporting so you don't just get "CLEX_parse_error"
|
||||
//
|
||||
// Contributors:
|
||||
// Arpad Goretity (bugfix)
|
||||
// Alan Hickman (hex floats)
|
||||
// github:mundusnine (bugfix)
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
#ifdef STB_C_LEXER_IMPLEMENTATION
|
||||
#ifndef STB_C_LEXER_DEFINITIONS
|
||||
// to change the default parsing rules, copy the following lines
|
||||
// into your C/C++ file *before* including this, and then replace
|
||||
// the Y's with N's for the ones you don't want. This needs to be
|
||||
// set to the same values for every place in your program where
|
||||
// stb_c_lexer.h is included.
|
||||
// --BEGIN--
|
||||
|
||||
#if defined(Y) || defined(N)
|
||||
#error "Can only use stb_c_lexer in contexts where the preprocessor symbols 'Y' and 'N' are not defined"
|
||||
#endif
|
||||
|
||||
#define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit
|
||||
#define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit
|
||||
#define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit
|
||||
#define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE][-+]?[0-9]+)?) CLEX_floatlit
|
||||
#define STB_C_LEX_C99_HEX_FLOATS N // "0x{hex}+(.{hex}*)?[pP][-+]?{hex}+ CLEX_floatlit
|
||||
#define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id
|
||||
#define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring
|
||||
#define STB_C_LEX_C_SQ_STRINGS N // single-quote-delimited strings with escapes CLEX_ssstring
|
||||
#define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits
|
||||
#define STB_C_LEX_C_COMMENTS Y // "/* comment */"
|
||||
#define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n"
|
||||
#define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq
|
||||
#define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror
|
||||
#define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr
|
||||
#define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus
|
||||
#define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow
|
||||
#define STB_C_LEX_EQUAL_ARROW N // "=>" CLEX_eqarrow
|
||||
#define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq
|
||||
#define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq
|
||||
// "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq
|
||||
// if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ:
|
||||
// "<<=" CLEX_shleq ">>=" CLEX_shreq
|
||||
|
||||
#define STB_C_LEX_PARSE_SUFFIXES N // letters after numbers are parsed as part of those numbers, and must be in suffix list below
|
||||
#define STB_C_LEX_DECIMAL_SUFFIXES "" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage
|
||||
#define STB_C_LEX_HEX_SUFFIXES "" // e.g. "uUlL"
|
||||
#define STB_C_LEX_OCTAL_SUFFIXES "" // e.g. "uUlL"
|
||||
#define STB_C_LEX_FLOAT_SUFFIXES "" //
|
||||
|
||||
#define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token
|
||||
#define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N
|
||||
#define STB_C_LEX_MULTILINE_DSTRINGS N // allow newlines in double-quoted strings
|
||||
#define STB_C_LEX_MULTILINE_SSTRINGS N // allow newlines in single-quoted strings
|
||||
#define STB_C_LEX_USE_STDLIB Y // use strtod,strtol for parsing #s; otherwise inaccurate hack
|
||||
#define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character
|
||||
#define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent
|
||||
|
||||
#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES N // if Y, all CLEX_ token names are defined, even if never returned
|
||||
// leaving it as N should help you catch config bugs
|
||||
|
||||
#define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess
|
||||
// still have #line, #pragma, etc)
|
||||
|
||||
//#define STB_C_LEX_ISWHITE(str) ... // return length in bytes of whitespace characters if first char is whitespace
|
||||
|
||||
#define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions
|
||||
// --END--
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef INCLUDE_STB_C_LEXER_H
|
||||
#define INCLUDE_STB_C_LEXER_H
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// lexer variables
|
||||
char *input_stream;
|
||||
char *eof;
|
||||
char *parse_point;
|
||||
char *string_storage;
|
||||
int string_storage_len;
|
||||
|
||||
// lexer parse location for error messages
|
||||
char *where_firstchar;
|
||||
char *where_lastchar;
|
||||
|
||||
// lexer token variables
|
||||
long token;
|
||||
double real_number;
|
||||
long int_number;
|
||||
char *string;
|
||||
int string_len;
|
||||
} stb_lexer;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int line_number;
|
||||
int line_offset;
|
||||
} stb_lex_location;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length);
|
||||
// this function initialize the 'lexer' structure
|
||||
// Input:
|
||||
// - input_stream points to the file to parse, loaded into memory
|
||||
// - input_stream_end points to the end of the file, or NULL if you use 0-for-EOF
|
||||
// - string_store is storage the lexer can use for storing parsed strings and identifiers
|
||||
// - store_length is the length of that storage
|
||||
|
||||
extern int stb_c_lexer_get_token(stb_lexer *lexer);
|
||||
// this function returns non-zero if a token is parsed, or 0 if at EOF
|
||||
// Output:
|
||||
// - lexer->token is the token ID, which is unicode code point for a single-char token, < 0 for a multichar or eof or error
|
||||
// - lexer->real_number is a double constant value for CLEX_floatlit, or CLEX_intlit if STB_C_LEX_INTEGERS_AS_DOUBLES
|
||||
// - lexer->int_number is an integer constant for CLEX_intlit if !STB_C_LEX_INTEGERS_AS_DOUBLES, or character for CLEX_charlit
|
||||
// - lexer->string is a 0-terminated string for CLEX_dqstring or CLEX_sqstring or CLEX_identifier
|
||||
// - lexer->string_len is the byte length of lexer->string
|
||||
|
||||
extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc);
|
||||
// this inefficient function returns the line number and character offset of a
|
||||
// given location in the file as returned by stb_lex_token. Because it's inefficient,
|
||||
// you should only call it for errors, not for every token.
|
||||
// For error messages of invalid tokens, you typically want the location of the start
|
||||
// of the token (which caused the token to be invalid). For bugs involving legit
|
||||
// tokens, you can report the first or the range.
|
||||
// Output:
|
||||
// - loc->line_number is the line number in the file, counting from 1, of the location
|
||||
// - loc->line_offset is the char-offset in the line, counting from 0, of the location
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
CLEX_eof = 256,
|
||||
CLEX_parse_error,
|
||||
CLEX_intlit ,
|
||||
CLEX_floatlit ,
|
||||
CLEX_id ,
|
||||
CLEX_dqstring ,
|
||||
CLEX_sqstring ,
|
||||
CLEX_charlit ,
|
||||
CLEX_eq ,
|
||||
CLEX_noteq ,
|
||||
CLEX_lesseq ,
|
||||
CLEX_greatereq ,
|
||||
CLEX_andand ,
|
||||
CLEX_oror ,
|
||||
CLEX_shl ,
|
||||
CLEX_shr ,
|
||||
CLEX_plusplus ,
|
||||
CLEX_minusminus ,
|
||||
CLEX_pluseq ,
|
||||
CLEX_minuseq ,
|
||||
CLEX_muleq ,
|
||||
CLEX_diveq ,
|
||||
CLEX_modeq ,
|
||||
CLEX_andeq ,
|
||||
CLEX_oreq ,
|
||||
CLEX_xoreq ,
|
||||
CLEX_arrow ,
|
||||
CLEX_eqarrow ,
|
||||
CLEX_shleq, CLEX_shreq,
|
||||
|
||||
CLEX_first_unused_token
|
||||
|
||||
};
|
||||
#endif // INCLUDE_STB_C_LEXER_H
|
||||
|
||||
#ifdef STB_C_LEXER_IMPLEMENTATION
|
||||
|
||||
// Hacky definitions so we can easily #if on them
|
||||
#define Y(x) 1
|
||||
#define N(x) 0
|
||||
|
||||
#if STB_C_LEX_INTEGERS_AS_DOUBLES(x)
|
||||
typedef double stb__clex_int;
|
||||
#define intfield real_number
|
||||
#define STB__clex_int_as_double
|
||||
#else
|
||||
typedef long stb__clex_int;
|
||||
#define intfield int_number
|
||||
#endif
|
||||
|
||||
// Convert these config options to simple conditional #defines so we can more
|
||||
// easily test them once we've change the meaning of Y/N
|
||||
|
||||
#if STB_C_LEX_PARSE_SUFFIXES(x)
|
||||
#define STB__clex_parse_suffixes
|
||||
#endif
|
||||
|
||||
#if STB_C_LEX_C99_HEX_FLOATS(x)
|
||||
#define STB__clex_hex_floats
|
||||
#endif
|
||||
|
||||
#if STB_C_LEX_C_HEX_INTS(x)
|
||||
#define STB__clex_hex_ints
|
||||
#endif
|
||||
|
||||
#if STB_C_LEX_C_DECIMAL_INTS(x)
|
||||
#define STB__clex_decimal_ints
|
||||
#endif
|
||||
|
||||
#if STB_C_LEX_C_OCTAL_INTS(x)
|
||||
#define STB__clex_octal_ints
|
||||
#endif
|
||||
|
||||
#if STB_C_LEX_C_DECIMAL_FLOATS(x)
|
||||
#define STB__clex_decimal_floats
|
||||
#endif
|
||||
|
||||
#if STB_C_LEX_DISCARD_PREPROCESSOR(x)
|
||||
#define STB__clex_discard_preprocessor
|
||||
#endif
|
||||
|
||||
#if STB_C_LEX_USE_STDLIB(x) && (!defined(STB__clex_hex_floats) || __STDC_VERSION__ >= 199901L)
|
||||
#define STB__CLEX_use_stdlib
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
// Now for the rest of the file we'll use the basic definition where
|
||||
// where Y expands to its contents and N expands to nothing
|
||||
#undef Y
|
||||
#define Y(a) a
|
||||
#undef N
|
||||
#define N(a)
|
||||
|
||||
// API function
|
||||
void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length)
|
||||
{
|
||||
lexer->input_stream = (char *) input_stream;
|
||||
lexer->eof = (char *) input_stream_end;
|
||||
lexer->parse_point = (char *) input_stream;
|
||||
lexer->string_storage = string_store;
|
||||
lexer->string_storage_len = store_length;
|
||||
}
|
||||
|
||||
// API function
|
||||
void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc)
|
||||
{
|
||||
char *p = lexer->input_stream;
|
||||
int line_number = 1;
|
||||
int char_offset = 0;
|
||||
while (*p && p < where) {
|
||||
if (*p == '\n' || *p == '\r') {
|
||||
p += (p[0]+p[1] == '\r'+'\n' ? 2 : 1); // skip newline
|
||||
line_number += 1;
|
||||
char_offset = 0;
|
||||
} else {
|
||||
++p;
|
||||
++char_offset;
|
||||
}
|
||||
}
|
||||
loc->line_number = line_number;
|
||||
loc->line_offset = char_offset;
|
||||
}
|
||||
|
||||
// main helper function for returning a parsed token
|
||||
static int stb__clex_token(stb_lexer *lexer, int token, char *start, char *end)
|
||||
{
|
||||
lexer->token = token;
|
||||
lexer->where_firstchar = start;
|
||||
lexer->where_lastchar = end;
|
||||
lexer->parse_point = end+1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// helper function for returning eof
|
||||
static int stb__clex_eof(stb_lexer *lexer)
|
||||
{
|
||||
lexer->token = CLEX_eof;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stb__clex_iswhite(int x)
|
||||
{
|
||||
return x == ' ' || x == '\t' || x == '\r' || x == '\n' || x == '\f';
|
||||
}
|
||||
|
||||
static const char *stb__strchr(const char *str, int ch)
|
||||
{
|
||||
for (; *str; ++str)
|
||||
if (*str == ch)
|
||||
return str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// parse suffixes at the end of a number
|
||||
static int stb__clex_parse_suffixes(stb_lexer *lexer, long tokenid, char *start, char *cur, const char *suffixes)
|
||||
{
|
||||
#ifdef STB__clex_parse_suffixes
|
||||
lexer->string = lexer->string_storage;
|
||||
lexer->string_len = 0;
|
||||
|
||||
while ((*cur >= 'a' && *cur <= 'z') || (*cur >= 'A' && *cur <= 'Z')) {
|
||||
if (stb__strchr(suffixes, *cur) == 0)
|
||||
return stb__clex_token(lexer, CLEX_parse_error, start, cur);
|
||||
if (lexer->string_len+1 >= lexer->string_storage_len)
|
||||
return stb__clex_token(lexer, CLEX_parse_error, start, cur);
|
||||
lexer->string[lexer->string_len++] = *cur++;
|
||||
}
|
||||
#else
|
||||
suffixes = suffixes; // attempt to suppress warnings
|
||||
#endif
|
||||
return stb__clex_token(lexer, tokenid, start, cur-1);
|
||||
}
|
||||
|
||||
#ifndef STB__CLEX_use_stdlib
|
||||
static double stb__clex_pow(double base, unsigned int exponent)
|
||||
{
|
||||
double value=1;
|
||||
for ( ; exponent; exponent >>= 1) {
|
||||
if (exponent & 1)
|
||||
value *= base;
|
||||
base *= base;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static double stb__clex_parse_float(char *p, char **q)
|
||||
{
|
||||
char *s = p;
|
||||
double value=0;
|
||||
int base=10;
|
||||
int exponent=0;
|
||||
|
||||
#ifdef STB__clex_hex_floats
|
||||
if (*p == '0') {
|
||||
if (p[1] == 'x' || p[1] == 'X') {
|
||||
base=16;
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
if (*p >= '0' && *p <= '9')
|
||||
value = value*base + (*p++ - '0');
|
||||
#ifdef STB__clex_hex_floats
|
||||
else if (base == 16 && *p >= 'a' && *p <= 'f')
|
||||
value = value*base + 10 + (*p++ - 'a');
|
||||
else if (base == 16 && *p >= 'A' && *p <= 'F')
|
||||
value = value*base + 10 + (*p++ - 'A');
|
||||
#endif
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p == '.') {
|
||||
double pow, addend = 0;
|
||||
++p;
|
||||
for (pow=1; ; pow*=base) {
|
||||
if (*p >= '0' && *p <= '9')
|
||||
addend = addend*base + (*p++ - '0');
|
||||
#ifdef STB__clex_hex_floats
|
||||
else if (base == 16 && *p >= 'a' && *p <= 'f')
|
||||
addend = addend*base + 10 + (*p++ - 'a');
|
||||
else if (base == 16 && *p >= 'A' && *p <= 'F')
|
||||
addend = addend*base + 10 + (*p++ - 'A');
|
||||
#endif
|
||||
else
|
||||
break;
|
||||
}
|
||||
value += addend / pow;
|
||||
}
|
||||
#ifdef STB__clex_hex_floats
|
||||
if (base == 16) {
|
||||
// exponent required for hex float literal
|
||||
if (*p != 'p' && *p != 'P') {
|
||||
*q = s;
|
||||
return 0;
|
||||
}
|
||||
exponent = 1;
|
||||
} else
|
||||
#endif
|
||||
exponent = (*p == 'e' || *p == 'E');
|
||||
|
||||
if (exponent) {
|
||||
int sign = p[1] == '-';
|
||||
unsigned int exponent=0;
|
||||
double power=1;
|
||||
++p;
|
||||
if (*p == '-' || *p == '+')
|
||||
++p;
|
||||
while (*p >= '0' && *p <= '9')
|
||||
exponent = exponent*10 + (*p++ - '0');
|
||||
|
||||
#ifdef STB__clex_hex_floats
|
||||
if (base == 16)
|
||||
power = stb__clex_pow(2, exponent);
|
||||
else
|
||||
#endif
|
||||
power = stb__clex_pow(10, exponent);
|
||||
if (sign)
|
||||
value /= power;
|
||||
else
|
||||
value *= power;
|
||||
}
|
||||
*q = p;
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int stb__clex_parse_char(char *p, char **q)
|
||||
{
|
||||
if (*p == '\\') {
|
||||
*q = p+2; // tentatively guess we'll parse two characters
|
||||
switch(p[1]) {
|
||||
case '\\': return '\\';
|
||||
case '\'': return '\'';
|
||||
case '"': return '"';
|
||||
case 't': return '\t';
|
||||
case 'f': return '\f';
|
||||
case 'n': return '\n';
|
||||
case 'r': return '\r';
|
||||
case '0': return '\0'; // @TODO ocatal constants
|
||||
case 'x': case 'X': return -1; // @TODO hex constants
|
||||
case 'u': return -1; // @TODO unicode constants
|
||||
}
|
||||
}
|
||||
*q = p+1;
|
||||
return (unsigned char) *p;
|
||||
}
|
||||
|
||||
static int stb__clex_parse_string(stb_lexer *lexer, char *p, int type)
|
||||
{
|
||||
char *start = p;
|
||||
char delim = *p++; // grab the " or ' for later matching
|
||||
char *out = lexer->string_storage;
|
||||
char *outend = lexer->string_storage + lexer->string_storage_len;
|
||||
while (*p != delim) {
|
||||
int n;
|
||||
if (*p == '\\') {
|
||||
char *q;
|
||||
n = stb__clex_parse_char(p, &q);
|
||||
if (n < 0)
|
||||
return stb__clex_token(lexer, CLEX_parse_error, start, q);
|
||||
p = q;
|
||||
} else {
|
||||
// @OPTIMIZE: could speed this up by looping-while-not-backslash
|
||||
n = (unsigned char) *p++;
|
||||
}
|
||||
if (out+1 > outend)
|
||||
return stb__clex_token(lexer, CLEX_parse_error, start, p);
|
||||
// @TODO expand unicode escapes to UTF8
|
||||
*out++ = (char) n;
|
||||
}
|
||||
*out = 0;
|
||||
lexer->string = lexer->string_storage;
|
||||
lexer->string_len = (int) (out - lexer->string_storage);
|
||||
return stb__clex_token(lexer, type, start, p);
|
||||
}
|
||||
|
||||
int stb_c_lexer_get_token(stb_lexer *lexer)
|
||||
{
|
||||
char *p = lexer->parse_point;
|
||||
|
||||
// skip whitespace and comments
|
||||
for (;;) {
|
||||
#ifdef STB_C_LEX_ISWHITE
|
||||
while (p != lexer->stream_end) {
|
||||
int n;
|
||||
n = STB_C_LEX_ISWHITE(p);
|
||||
if (n == 0) break;
|
||||
if (lexer->eof && lexer->eof - lexer->parse_point < n)
|
||||
return stb__clex_token(tok, CLEX_parse_error, p,lexer->eof-1);
|
||||
p += n;
|
||||
}
|
||||
#else
|
||||
while (p != lexer->eof && stb__clex_iswhite(*p))
|
||||
++p;
|
||||
#endif
|
||||
|
||||
STB_C_LEX_CPP_COMMENTS(
|
||||
if (p != lexer->eof && p[0] == '/' && p[1] == '/') {
|
||||
while (p != lexer->eof && *p != '\r' && *p != '\n')
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
)
|
||||
|
||||
STB_C_LEX_C_COMMENTS(
|
||||
if (p != lexer->eof && p[0] == '/' && p[1] == '*') {
|
||||
char *start = p;
|
||||
p += 2;
|
||||
while (p != lexer->eof && (p[0] != '*' || p[1] != '/'))
|
||||
++p;
|
||||
if (p == lexer->eof)
|
||||
return stb__clex_token(lexer, CLEX_parse_error, start, p-1);
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
)
|
||||
|
||||
#ifdef STB__clex_discard_preprocessor
|
||||
// @TODO this discards everything after a '#', regardless
|
||||
// of where in the line the # is, rather than requiring it
|
||||
// be at the start. (because this parser doesn't otherwise
|
||||
// check for line breaks!)
|
||||
if (p != lexer->eof && p[0] == '#') {
|
||||
while (p != lexer->eof && *p != '\r' && *p != '\n')
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == lexer->eof)
|
||||
return stb__clex_eof(lexer);
|
||||
|
||||
switch (*p) {
|
||||
default:
|
||||
if ( (*p >= 'a' && *p <= 'z')
|
||||
|| (*p >= 'A' && *p <= 'Z')
|
||||
|| *p == '_' || (unsigned char) *p >= 128 // >= 128 is UTF8 char
|
||||
STB_C_LEX_DOLLAR_IDENTIFIER( || *p == '$' ) )
|
||||
{
|
||||
int n = 0;
|
||||
lexer->string = lexer->string_storage;
|
||||
do {
|
||||
if (n+1 >= lexer->string_storage_len)
|
||||
return stb__clex_token(lexer, CLEX_parse_error, p, p+n);
|
||||
lexer->string[n] = p[n];
|
||||
++n;
|
||||
} while (
|
||||
(p[n] >= 'a' && p[n] <= 'z')
|
||||
|| (p[n] >= 'A' && p[n] <= 'Z')
|
||||
|| (p[n] >= '0' && p[n] <= '9') // allow digits in middle of identifier
|
||||
|| p[n] == '_' || (unsigned char) p[n] >= 128
|
||||
STB_C_LEX_DOLLAR_IDENTIFIER( || p[n] == '$' )
|
||||
);
|
||||
lexer->string[n] = 0;
|
||||
lexer->string_len = n;
|
||||
return stb__clex_token(lexer, CLEX_id, p, p+n-1);
|
||||
}
|
||||
|
||||
// check for EOF
|
||||
STB_C_LEX_0_IS_EOF(
|
||||
if (*p == 0)
|
||||
return stb__clex_eof(lexer);
|
||||
)
|
||||
|
||||
single_char:
|
||||
// not an identifier, return the character as itself
|
||||
return stb__clex_token(lexer, *p, p, p);
|
||||
|
||||
case '+':
|
||||
if (p+1 != lexer->eof) {
|
||||
STB_C_LEX_C_INCREMENTS(if (p[1] == '+') return stb__clex_token(lexer, CLEX_plusplus, p,p+1);)
|
||||
STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_pluseq , p,p+1);)
|
||||
}
|
||||
goto single_char;
|
||||
case '-':
|
||||
if (p+1 != lexer->eof) {
|
||||
STB_C_LEX_C_INCREMENTS(if (p[1] == '-') return stb__clex_token(lexer, CLEX_minusminus, p,p+1);)
|
||||
STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_minuseq , p,p+1);)
|
||||
STB_C_LEX_C_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_arrow , p,p+1);)
|
||||
}
|
||||
goto single_char;
|
||||
case '&':
|
||||
if (p+1 != lexer->eof) {
|
||||
STB_C_LEX_C_LOGICAL( if (p[1] == '&') return stb__clex_token(lexer, CLEX_andand, p,p+1);)
|
||||
STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_andeq , p,p+1);)
|
||||
}
|
||||
goto single_char;
|
||||
case '|':
|
||||
if (p+1 != lexer->eof) {
|
||||
STB_C_LEX_C_LOGICAL( if (p[1] == '|') return stb__clex_token(lexer, CLEX_oror, p,p+1);)
|
||||
STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_oreq, p,p+1);)
|
||||
}
|
||||
goto single_char;
|
||||
case '=':
|
||||
if (p+1 != lexer->eof) {
|
||||
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_eq, p,p+1);)
|
||||
STB_C_LEX_EQUAL_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_eqarrow, p,p+1);)
|
||||
}
|
||||
goto single_char;
|
||||
case '!':
|
||||
STB_C_LEX_C_COMPARISONS(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_noteq, p,p+1);)
|
||||
goto single_char;
|
||||
case '^':
|
||||
STB_C_LEX_C_BITWISEEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_xoreq, p,p+1));
|
||||
goto single_char;
|
||||
case '%':
|
||||
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_modeq, p,p+1));
|
||||
goto single_char;
|
||||
case '*':
|
||||
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_muleq, p,p+1));
|
||||
goto single_char;
|
||||
case '/':
|
||||
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_diveq, p,p+1));
|
||||
goto single_char;
|
||||
case '<':
|
||||
if (p+1 != lexer->eof) {
|
||||
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_lesseq, p,p+1);)
|
||||
STB_C_LEX_C_SHIFTS( if (p[1] == '<') {
|
||||
STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=')
|
||||
return stb__clex_token(lexer, CLEX_shleq, p,p+2);)
|
||||
return stb__clex_token(lexer, CLEX_shl, p,p+1);
|
||||
}
|
||||
)
|
||||
}
|
||||
goto single_char;
|
||||
case '>':
|
||||
if (p+1 != lexer->eof) {
|
||||
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_greatereq, p,p+1);)
|
||||
STB_C_LEX_C_SHIFTS( if (p[1] == '>') {
|
||||
STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=')
|
||||
return stb__clex_token(lexer, CLEX_shreq, p,p+2);)
|
||||
return stb__clex_token(lexer, CLEX_shr, p,p+1);
|
||||
}
|
||||
)
|
||||
}
|
||||
goto single_char;
|
||||
|
||||
case '"':
|
||||
STB_C_LEX_C_DQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_dqstring);)
|
||||
goto single_char;
|
||||
case '\'':
|
||||
STB_C_LEX_C_SQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_sqstring);)
|
||||
STB_C_LEX_C_CHARS(
|
||||
{
|
||||
char *start = p;
|
||||
lexer->int_number = stb__clex_parse_char(p+1, &p);
|
||||
if (lexer->int_number < 0)
|
||||
return stb__clex_token(lexer, CLEX_parse_error, start,start);
|
||||
if (p == lexer->eof || *p != '\'')
|
||||
return stb__clex_token(lexer, CLEX_parse_error, start,p);
|
||||
return stb__clex_token(lexer, CLEX_charlit, start, p+1);
|
||||
})
|
||||
goto single_char;
|
||||
|
||||
case '0':
|
||||
#if defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats)
|
||||
if (p+1 != lexer->eof) {
|
||||
if (p[1] == 'x' || p[1] == 'X') {
|
||||
char *q;
|
||||
|
||||
#ifdef STB__clex_hex_floats
|
||||
for (q=p+2;
|
||||
q != lexer->eof && ((*q >= '0' && *q <= '9') || (*q >= 'a' && *q <= 'f') || (*q >= 'A' && *q <= 'F'));
|
||||
++q);
|
||||
if (q != lexer->eof) {
|
||||
if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'p' || *q == 'P')) {
|
||||
#ifdef STB__CLEX_use_stdlib
|
||||
lexer->real_number = strtod((char *) p, (char**) &q);
|
||||
#else
|
||||
lexer->real_number = stb__clex_parse_float(p, &q);
|
||||
#endif
|
||||
|
||||
if (p == q)
|
||||
return stb__clex_token(lexer, CLEX_parse_error, p,q);
|
||||
return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES);
|
||||
|
||||
}
|
||||
}
|
||||
#endif // STB__CLEX_hex_floats
|
||||
|
||||
#ifdef STB__clex_hex_ints
|
||||
#ifdef STB__CLEX_use_stdlib
|
||||
lexer->int_number = strtol((char *) p, (char **) &q, 16);
|
||||
#else
|
||||
{
|
||||
stb__clex_int n=0;
|
||||
for (q=p+2; q != lexer->eof; ++q) {
|
||||
if (*q >= '0' && *q <= '9')
|
||||
n = n*16 + (*q - '0');
|
||||
else if (*q >= 'a' && *q <= 'f')
|
||||
n = n*16 + (*q - 'a') + 10;
|
||||
else if (*q >= 'A' && *q <= 'F')
|
||||
n = n*16 + (*q - 'A') + 10;
|
||||
else
|
||||
break;
|
||||
}
|
||||
lexer->int_number = n;
|
||||
}
|
||||
#endif
|
||||
if (q == p+2)
|
||||
return stb__clex_token(lexer, CLEX_parse_error, p-2,p-1);
|
||||
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_HEX_SUFFIXES);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif // defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats)
|
||||
// can't test for octal because we might parse '0.0' as float or as '0' '.' '0',
|
||||
// so have to do float first
|
||||
|
||||
/* FALL THROUGH */
|
||||
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
||||
|
||||
#ifdef STB__clex_decimal_floats
|
||||
{
|
||||
char *q = p;
|
||||
while (q != lexer->eof && (*q >= '0' && *q <= '9'))
|
||||
++q;
|
||||
if (q != lexer->eof) {
|
||||
if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'e' || *q == 'E')) {
|
||||
#ifdef STB__CLEX_use_stdlib
|
||||
lexer->real_number = strtod((char *) p, (char**) &q);
|
||||
#else
|
||||
lexer->real_number = stb__clex_parse_float(p, &q);
|
||||
#endif
|
||||
|
||||
return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // STB__clex_decimal_floats
|
||||
|
||||
#ifdef STB__clex_octal_ints
|
||||
if (p[0] == '0') {
|
||||
char *q = p;
|
||||
#ifdef STB__CLEX_use_stdlib
|
||||
lexer->int_number = strtol((char *) p, (char **) &q, 8);
|
||||
#else
|
||||
stb__clex_int n=0;
|
||||
while (q != lexer->eof) {
|
||||
if (*q >= '0' && *q <= '7')
|
||||
n = n*8 + (*q - '0');
|
||||
else
|
||||
break;
|
||||
++q;
|
||||
}
|
||||
if (q != lexer->eof && (*q == '8' || *q=='9'))
|
||||
return stb__clex_token(lexer, CLEX_parse_error, p, q);
|
||||
lexer->int_number = n;
|
||||
#endif
|
||||
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
|
||||
}
|
||||
#endif // STB__clex_octal_ints
|
||||
|
||||
#ifdef STB__clex_decimal_ints
|
||||
{
|
||||
char *q = p;
|
||||
#ifdef STB__CLEX_use_stdlib
|
||||
lexer->int_number = strtol((char *) p, (char **) &q, 10);
|
||||
#else
|
||||
stb__clex_int n=0;
|
||||
while (q != lexer->eof) {
|
||||
if (*q >= '0' && *q <= '9')
|
||||
n = n*10 + (*q - '0');
|
||||
else
|
||||
break;
|
||||
++q;
|
||||
}
|
||||
lexer->int_number = n;
|
||||
#endif
|
||||
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
|
||||
}
|
||||
#endif // STB__clex_decimal_ints
|
||||
goto single_char;
|
||||
}
|
||||
}
|
||||
#endif // STB_C_LEXER_IMPLEMENTATION
|
||||
|
||||
#ifdef STB_C_LEXER_SELF_TEST
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void print_token(stb_lexer *lexer)
|
||||
{
|
||||
switch (lexer->token) {
|
||||
case CLEX_id : printf("_%s", lexer->string); break;
|
||||
case CLEX_eq : printf("=="); break;
|
||||
case CLEX_noteq : printf("!="); break;
|
||||
case CLEX_lesseq : printf("<="); break;
|
||||
case CLEX_greatereq : printf(">="); break;
|
||||
case CLEX_andand : printf("&&"); break;
|
||||
case CLEX_oror : printf("||"); break;
|
||||
case CLEX_shl : printf("<<"); break;
|
||||
case CLEX_shr : printf(">>"); break;
|
||||
case CLEX_plusplus : printf("++"); break;
|
||||
case CLEX_minusminus: printf("--"); break;
|
||||
case CLEX_arrow : printf("->"); break;
|
||||
case CLEX_andeq : printf("&="); break;
|
||||
case CLEX_oreq : printf("|="); break;
|
||||
case CLEX_xoreq : printf("^="); break;
|
||||
case CLEX_pluseq : printf("+="); break;
|
||||
case CLEX_minuseq : printf("-="); break;
|
||||
case CLEX_muleq : printf("*="); break;
|
||||
case CLEX_diveq : printf("/="); break;
|
||||
case CLEX_modeq : printf("%%="); break;
|
||||
case CLEX_shleq : printf("<<="); break;
|
||||
case CLEX_shreq : printf(">>="); break;
|
||||
case CLEX_eqarrow : printf("=>"); break;
|
||||
case CLEX_dqstring : printf("\"%s\"", lexer->string); break;
|
||||
case CLEX_sqstring : printf("'\"%s\"'", lexer->string); break;
|
||||
case CLEX_charlit : printf("'%s'", lexer->string); break;
|
||||
#if defined(STB__clex_int_as_double) && !defined(STB__CLEX_use_stdlib)
|
||||
case CLEX_intlit : printf("#%g", lexer->real_number); break;
|
||||
#else
|
||||
case CLEX_intlit : printf("#%ld", lexer->int_number); break;
|
||||
#endif
|
||||
case CLEX_floatlit : printf("%g", lexer->real_number); break;
|
||||
default:
|
||||
if (lexer->token >= 0 && lexer->token < 256)
|
||||
printf("%c", (int) lexer->token);
|
||||
else {
|
||||
printf("<<<UNKNOWN TOKEN %ld >>>\n", lexer->token);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force a test
|
||||
of parsing
|
||||
multiline comments */
|
||||
|
||||
/*/ comment /*/
|
||||
/**/ extern /**/
|
||||
|
||||
void dummy(void)
|
||||
{
|
||||
double some_floats[] = {
|
||||
1.0501, -10.4e12, 5E+10,
|
||||
#if 0 // not supported in C++ or C-pre-99, so don't try to compile it, but let our parser test it
|
||||
0x1.0p+24, 0xff.FP-8, 0x1p-23,
|
||||
#endif
|
||||
4.
|
||||
};
|
||||
(void) sizeof(some_floats);
|
||||
(void) some_floats[1];
|
||||
|
||||
printf("test %d",1); // https://github.com/nothings/stb/issues/13
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
FILE *f = fopen("stb_c_lexer.h","rb");
|
||||
char *text = (char *) malloc(1 << 20);
|
||||
int len = f ? (int) fread(text, 1, 1<<20, f) : -1;
|
||||
stb_lexer lex;
|
||||
if (len < 0) {
|
||||
fprintf(stderr, "Error opening file\n");
|
||||
free(text);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
stb_c_lexer_init(&lex, text, text+len, (char *) malloc(0x10000), 0x10000);
|
||||
while (stb_c_lexer_get_token(&lex)) {
|
||||
if (lex.token == CLEX_parse_error) {
|
||||
printf("\n<<<PARSE ERROR>>>\n");
|
||||
break;
|
||||
}
|
||||
print_token(&lex);
|
||||
printf(" ");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
1049
thirdparty/stb/stb_connected_components.h
vendored
Normal file
433
thirdparty/stb/stb_divide.h
vendored
Normal file
@@ -0,0 +1,433 @@
|
||||
// stb_divide.h - v0.94 - public domain - Sean Barrett, Feb 2010
|
||||
// Three kinds of divide/modulus of signed integers.
|
||||
//
|
||||
// HISTORY
|
||||
//
|
||||
// v0.94 Fix integer overflow issues
|
||||
// v0.93 2020-02-02 Write useful exit() value from main()
|
||||
// v0.92 2019-02-25 Fix warning
|
||||
// v0.91 2010-02-27 Fix euclidean division by INT_MIN for non-truncating C
|
||||
// Check result with 64-bit math to catch such cases
|
||||
// v0.90 2010-02-24 First public release
|
||||
//
|
||||
// USAGE
|
||||
//
|
||||
// In *ONE* source file, put:
|
||||
//
|
||||
// #define STB_DIVIDE_IMPLEMENTATION
|
||||
// // #define C_INTEGER_DIVISION_TRUNCATES // see Note 1
|
||||
// // #define C_INTEGER_DIVISION_FLOORS // see Note 2
|
||||
// #include "stb_divide.h"
|
||||
//
|
||||
// Other source files should just include stb_divide.h
|
||||
//
|
||||
// Note 1: On platforms/compilers that you know signed C division
|
||||
// truncates, you can #define C_INTEGER_DIVISION_TRUNCATES.
|
||||
//
|
||||
// Note 2: On platforms/compilers that you know signed C division
|
||||
// floors (rounds to negative infinity), you can #define
|
||||
// C_INTEGER_DIVISION_FLOORS.
|
||||
//
|
||||
// You can #define STB_DIVIDE_TEST in which case the implementation
|
||||
// will generate a main() and compiling the result will create a
|
||||
// program that tests the implementation. Run it with no arguments
|
||||
// and any output indicates an error; run it with any argument and
|
||||
// it will also print the test results. Define STB_DIVIDE_TEST_64
|
||||
// to a 64-bit integer type to avoid overflows in the result-checking
|
||||
// which give false negatives.
|
||||
//
|
||||
// ABOUT
|
||||
//
|
||||
// This file provides three different consistent divide/mod pairs
|
||||
// implemented on top of arbitrary C/C++ division, including correct
|
||||
// handling of overflow of intermediate calculations:
|
||||
//
|
||||
// trunc: a/b truncates to 0, a%b has same sign as a
|
||||
// floor: a/b truncates to -inf, a%b has same sign as b
|
||||
// eucl: a/b truncates to sign(b)*inf, a%b is non-negative
|
||||
//
|
||||
// Not necessarily optimal; I tried to keep it generally efficient,
|
||||
// but there may be better ways.
|
||||
//
|
||||
// Briefly, for those who are not familiar with the problem, we note
|
||||
// the reason these divides exist and are interesting:
|
||||
//
|
||||
// 'trunc' is easy to implement in hardware (strip the signs,
|
||||
// compute, reapply the signs), thus is commonly defined
|
||||
// by many languages (including C99)
|
||||
//
|
||||
// 'floor' is simple to define and better behaved than trunc;
|
||||
// for example it divides integers into fixed-size buckets
|
||||
// without an extra-wide bucket at 0, and for a fixed
|
||||
// divisor N there are only |N| possible moduli.
|
||||
//
|
||||
// 'eucl' guarantees fixed-sized buckets *and* a non-negative
|
||||
// modulus and defines division to be whatever is needed
|
||||
// to achieve that result.
|
||||
//
|
||||
// See "The Euclidean definition of the functions div and mod"
|
||||
// by Raymond Boute (1992), or "Division and Modulus for Computer
|
||||
// Scientists" by Daan Leijen (2001)
|
||||
//
|
||||
// We assume of the built-in C division:
|
||||
// (a) modulus is the remainder for the corresponding division
|
||||
// (b) a/b truncates if a and b are the same sign
|
||||
//
|
||||
// Property (a) requires (a/b)*b + (a%b)==a, and is required by C.
|
||||
// Property (b) seems to be true of all hardware but is *not* satisfied
|
||||
// by the euclidean division operator we define, so it's possibly not
|
||||
// always true. If any such platform turns up, we can add more cases.
|
||||
// (Possibly only stb_div_trunc currently relies on property (b).)
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
|
||||
#ifndef INCLUDE_STB_DIVIDE_H
|
||||
#define INCLUDE_STB_DIVIDE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by);
|
||||
extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by);
|
||||
extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by);
|
||||
extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by);
|
||||
extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by);
|
||||
extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef STB_DIVIDE_IMPLEMENTATION
|
||||
|
||||
#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901
|
||||
#ifndef C_INTEGER_DIVISION_TRUNCATES
|
||||
#define C_INTEGER_DIVISION_TRUNCATES
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef INT_MIN
|
||||
#include <limits.h> // if you have no limits.h, #define INT_MIN yourself
|
||||
#endif
|
||||
|
||||
// the following macros are designed to allow testing
|
||||
// other platforms by simulating them
|
||||
#ifndef STB_DIVIDE_TEST_FLOOR
|
||||
#define stb__div(a,b) ((a)/(b))
|
||||
#define stb__mod(a,b) ((a)%(b))
|
||||
#else
|
||||
// implement floor-style divide on trunc platform
|
||||
#ifndef C_INTEGER_DIVISION_TRUNCATES
|
||||
#error "floor test requires truncating division"
|
||||
#endif
|
||||
#undef C_INTEGER_DIVISION_TRUNCATES
|
||||
int stb__div(int v1, int v2)
|
||||
{
|
||||
int q = v1/v2, r = v1%v2;
|
||||
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
|
||||
return q-1;
|
||||
else
|
||||
return q;
|
||||
}
|
||||
|
||||
int stb__mod(int v1, int v2)
|
||||
{
|
||||
int r = v1%v2;
|
||||
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
|
||||
return r+v2;
|
||||
else
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
int stb_div_trunc(int v1, int v2)
|
||||
{
|
||||
#ifdef C_INTEGER_DIVISION_TRUNCATES
|
||||
return v1/v2;
|
||||
#else
|
||||
if (v1 >= 0 && v2 <= 0)
|
||||
return -stb__div(-v1,v2); // both negative to avoid overflow
|
||||
if (v1 <= 0 && v2 >= 0)
|
||||
if (v1 != INT_MIN)
|
||||
return -stb__div(v1,-v2); // both negative to avoid overflow
|
||||
else
|
||||
return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point
|
||||
else
|
||||
return v1/v2; // same sign, so expect truncation
|
||||
#endif
|
||||
}
|
||||
|
||||
int stb_div_floor(int v1, int v2)
|
||||
{
|
||||
#ifdef C_INTEGER_DIVISION_FLOORS
|
||||
return v1/v2;
|
||||
#else
|
||||
if (v1 >= 0 && v2 < 0) {
|
||||
if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows
|
||||
return -stb__div((v2+1)-v1,v2); // nope, so just compute it
|
||||
else
|
||||
return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0);
|
||||
}
|
||||
if (v1 < 0 && v2 >= 0) {
|
||||
if (v1 != INT_MIN) {
|
||||
if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows
|
||||
return -stb__div((v1+1)-v2,-v2); // nope, so just compute it
|
||||
else
|
||||
return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0);
|
||||
} else // it must be possible to compute -(v1+v2) without overflowing
|
||||
return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1);
|
||||
} else
|
||||
return v1/v2; // same sign, so expect truncation
|
||||
#endif
|
||||
}
|
||||
|
||||
int stb_div_eucl(int v1, int v2)
|
||||
{
|
||||
int q,r;
|
||||
#ifdef C_INTEGER_DIVISION_TRUNCATES
|
||||
q = v1/v2;
|
||||
r = v1%v2;
|
||||
#else
|
||||
// handle every quadrant separately, since we can't rely on q and r flor
|
||||
if (v1 >= 0)
|
||||
if (v2 >= 0)
|
||||
return stb__div(v1,v2);
|
||||
else if (v2 != INT_MIN)
|
||||
q = -stb__div(v1,-v2), r = stb__mod(v1,-v2);
|
||||
else
|
||||
q = 0, r = v1;
|
||||
else if (v1 != INT_MIN)
|
||||
if (v2 >= 0)
|
||||
q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2);
|
||||
else if (v2 != INT_MIN)
|
||||
q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2);
|
||||
else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2
|
||||
q = 1, r = v1-q*v2;
|
||||
else // if v1 is INT_MIN, we have to move away from overflow place
|
||||
if (v2 >= 0)
|
||||
q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2);
|
||||
else if (v2 != INT_MIN)
|
||||
q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2);
|
||||
else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow
|
||||
q = 1, r = 0;
|
||||
#endif
|
||||
if (r >= 0)
|
||||
return q;
|
||||
else
|
||||
return q + (v2 > 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
int stb_mod_trunc(int v1, int v2)
|
||||
{
|
||||
#ifdef C_INTEGER_DIVISION_TRUNCATES
|
||||
return v1%v2;
|
||||
#else
|
||||
if (v1 >= 0) { // modulus result should always be positive
|
||||
int r = stb__mod(v1,v2);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
else
|
||||
return r - (v2 < 0 ? v2 : -v2);
|
||||
} else { // modulus result should always be negative
|
||||
int r = stb__mod(v1,v2);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
else
|
||||
return r + (v2 < 0 ? v2 : -v2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int stb_mod_floor(int v1, int v2)
|
||||
{
|
||||
#ifdef C_INTEGER_DIVISION_FLOORS
|
||||
return v1%v2;
|
||||
#else
|
||||
if (v2 >= 0) { // result should always be positive
|
||||
int r = stb__mod(v1,v2);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
else
|
||||
return r + v2;
|
||||
} else { // result should always be negative
|
||||
int r = stb__mod(v1,v2);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
else
|
||||
return r + v2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int stb_mod_eucl(int v1, int v2)
|
||||
{
|
||||
int r = stb__mod(v1,v2);
|
||||
|
||||
if (r >= 0)
|
||||
return r;
|
||||
else
|
||||
return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow]
|
||||
}
|
||||
|
||||
#ifdef STB_DIVIDE_TEST
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
int show=0;
|
||||
int err=0;
|
||||
|
||||
void stbdiv_check(int q, int r, int a, int b, char *type, int dir)
|
||||
{
|
||||
if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) {
|
||||
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r);
|
||||
err++;
|
||||
} else
|
||||
if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid
|
||||
if (r <= -abs(b) || r >= abs(b)) {
|
||||
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r);
|
||||
err++;
|
||||
}
|
||||
#ifdef STB_DIVIDE_TEST_64
|
||||
{
|
||||
STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b;
|
||||
if (q64*b64+r64 != a64) {
|
||||
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
|
||||
err++;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (q*b+r != a) {
|
||||
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
|
||||
err++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void test(int a, int b)
|
||||
{
|
||||
int q,r;
|
||||
if (show) printf("(%+11d,%+d) | ", a,b);
|
||||
q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b);
|
||||
if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "trunc",a);
|
||||
q = stb_div_floor(a,b), r = stb_mod_floor(a,b);
|
||||
if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "floor",b);
|
||||
q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b);
|
||||
if (show) printf("(%+11d,%+2d)\n", q,r); stbdiv_check(q,r,a,b, "euclidean",1);
|
||||
}
|
||||
|
||||
void testh(int a, int b)
|
||||
{
|
||||
int q,r;
|
||||
if (show) printf("(%08x,%08x) |\n", a,b);
|
||||
q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); stbdiv_check(q,r,a,b, "trunc",a);
|
||||
if (show) printf(" (%08x,%08x)", q,r);
|
||||
q = stb_div_floor(a,b), r = stb_mod_floor(a,b); stbdiv_check(q,r,a,b, "floor",b);
|
||||
if (show) printf(" (%08x,%08x)", q,r);
|
||||
q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); stbdiv_check(q,r,a,b, "euclidean",1);
|
||||
if (show) printf(" (%08x,%08x)\n ", q,r);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc > 1) show=1;
|
||||
|
||||
test(8,3);
|
||||
test(8,-3);
|
||||
test(-8,3);
|
||||
test(-8,-3);
|
||||
test(1,2);
|
||||
test(1,-2);
|
||||
test(-1,2);
|
||||
test(-1,-2);
|
||||
test(8,4);
|
||||
test(8,-4);
|
||||
test(-8,4);
|
||||
test(-8,-4);
|
||||
|
||||
test(INT_MAX,1);
|
||||
test(INT_MIN,1);
|
||||
test(INT_MIN+1,1);
|
||||
test(INT_MAX,-1);
|
||||
//test(INT_MIN,-1); // this traps in MSVC, so we leave it untested
|
||||
test(INT_MIN+1,-1);
|
||||
test(INT_MIN,-2);
|
||||
test(INT_MIN+1,2);
|
||||
test(INT_MIN+1,-2);
|
||||
test(INT_MAX,2);
|
||||
test(INT_MAX,-2);
|
||||
test(INT_MIN+1,2);
|
||||
test(INT_MIN+1,-2);
|
||||
test(INT_MIN,2);
|
||||
test(INT_MIN,-2);
|
||||
test(INT_MIN,7);
|
||||
test(INT_MIN,-7);
|
||||
test(INT_MIN+1,4);
|
||||
test(INT_MIN+1,-4);
|
||||
|
||||
testh(-7, INT_MIN);
|
||||
testh(-1, INT_MIN);
|
||||
testh(1, INT_MIN);
|
||||
testh(7, INT_MIN);
|
||||
|
||||
testh(INT_MAX-1, INT_MIN);
|
||||
testh(INT_MAX, INT_MIN);
|
||||
testh(INT_MIN, INT_MIN);
|
||||
testh(INT_MIN+1, INT_MIN);
|
||||
|
||||
testh(INT_MAX-1, INT_MAX);
|
||||
testh(INT_MAX , INT_MAX);
|
||||
testh(INT_MIN , INT_MAX);
|
||||
testh(INT_MIN+1, INT_MAX);
|
||||
|
||||
return err > 0 ? 1 : 0;
|
||||
}
|
||||
#endif // STB_DIVIDE_TEST
|
||||
#endif // STB_DIVIDE_IMPLEMENTATION
|
||||
#endif // INCLUDE_STB_DIVIDE_H
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
1895
thirdparty/stb/stb_ds.h
vendored
Normal file
719
thirdparty/stb/stb_dxt.h
vendored
Normal file
@@ -0,0 +1,719 @@
|
||||
// stb_dxt.h - v1.12 - DXT1/DXT5 compressor - public domain
|
||||
// original by fabian "ryg" giesen - ported to C by stb
|
||||
// use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation
|
||||
//
|
||||
// USAGE:
|
||||
// call stb_compress_dxt_block() for every block (you must pad)
|
||||
// source should be a 4x4 block of RGBA data in row-major order;
|
||||
// Alpha channel is not stored if you specify alpha=0 (but you
|
||||
// must supply some constant alpha in the alpha channel).
|
||||
// You can turn on dithering and "high quality" using mode.
|
||||
//
|
||||
// version history:
|
||||
// v1.12 - (ryg) fix bug in single-color table generator
|
||||
// v1.11 - (ryg) avoid racy global init, better single-color tables, remove dither
|
||||
// v1.10 - (i.c) various small quality improvements
|
||||
// v1.09 - (stb) update documentation re: surprising alpha channel requirement
|
||||
// v1.08 - (stb) fix bug in dxt-with-alpha block
|
||||
// v1.07 - (stb) bc4; allow not using libc; add STB_DXT_STATIC
|
||||
// v1.06 - (stb) fix to known-broken 1.05
|
||||
// v1.05 - (stb) support bc5/3dc (Arvids Kokins), use extern "C" in C++ (Pavel Krajcevski)
|
||||
// v1.04 - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec);
|
||||
// single color match fix (allow for inexact color interpolation);
|
||||
// optimal DXT5 index finder; "high quality" mode that runs multiple refinement steps.
|
||||
// v1.03 - (stb) endianness support
|
||||
// v1.02 - (stb) fix alpha encoding bug
|
||||
// v1.01 - (stb) fix bug converting to RGB that messed up quality, thanks ryg & cbloom
|
||||
// v1.00 - (stb) first release
|
||||
//
|
||||
// contributors:
|
||||
// Rich Geldreich (more accurate index selection)
|
||||
// Kevin Schmidt (#defines for "freestanding" compilation)
|
||||
// github:ppiastucki (BC4 support)
|
||||
// Ignacio Castano - improve DXT endpoint quantization
|
||||
// Alan Hickman - static table initialization
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
#ifndef STB_INCLUDE_STB_DXT_H
|
||||
#define STB_INCLUDE_STB_DXT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef STB_DXT_STATIC
|
||||
#define STBDDEF static
|
||||
#else
|
||||
#define STBDDEF extern
|
||||
#endif
|
||||
|
||||
// compression mode (bitflags)
|
||||
#define STB_DXT_NORMAL 0
|
||||
#define STB_DXT_DITHER 1 // use dithering. was always dubious, now deprecated. does nothing!
|
||||
#define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower.
|
||||
|
||||
STBDDEF void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src_rgba_four_bytes_per_pixel, int alpha, int mode);
|
||||
STBDDEF void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src_r_one_byte_per_pixel);
|
||||
STBDDEF void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src_rg_two_byte_per_pixel);
|
||||
|
||||
#define STB_COMPRESS_DXT_BLOCK
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // STB_INCLUDE_STB_DXT_H
|
||||
|
||||
#ifdef STB_DXT_IMPLEMENTATION
|
||||
|
||||
// configuration options for DXT encoder. set them in the project/makefile or just define
|
||||
// them at the top.
|
||||
|
||||
// STB_DXT_USE_ROUNDING_BIAS
|
||||
// use a rounding bias during color interpolation. this is closer to what "ideal"
|
||||
// interpolation would do but doesn't match the S3TC/DX10 spec. old versions (pre-1.03)
|
||||
// implicitly had this turned on.
|
||||
//
|
||||
// in case you're targeting a specific type of hardware (e.g. console programmers):
|
||||
// NVidia and Intel GPUs (as of 2010) as well as DX9 ref use DXT decoders that are closer
|
||||
// to STB_DXT_USE_ROUNDING_BIAS. AMD/ATI, S3 and DX10 ref are closer to rounding with no bias.
|
||||
// you also see "(a*5 + b*3) / 8" on some old GPU designs.
|
||||
// #define STB_DXT_USE_ROUNDING_BIAS
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if !defined(STBD_FABS)
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
#ifndef STBD_FABS
|
||||
#define STBD_FABS(x) fabs(x)
|
||||
#endif
|
||||
|
||||
static const unsigned char stb__OMatch5[256][2] = {
|
||||
{ 0, 0 }, { 0, 0 }, { 0, 1 }, { 0, 1 }, { 1, 0 }, { 1, 0 }, { 1, 0 }, { 1, 1 },
|
||||
{ 1, 1 }, { 1, 1 }, { 1, 2 }, { 0, 4 }, { 2, 1 }, { 2, 1 }, { 2, 1 }, { 2, 2 },
|
||||
{ 2, 2 }, { 2, 2 }, { 2, 3 }, { 1, 5 }, { 3, 2 }, { 3, 2 }, { 4, 0 }, { 3, 3 },
|
||||
{ 3, 3 }, { 3, 3 }, { 3, 4 }, { 3, 4 }, { 3, 4 }, { 3, 5 }, { 4, 3 }, { 4, 3 },
|
||||
{ 5, 2 }, { 4, 4 }, { 4, 4 }, { 4, 5 }, { 4, 5 }, { 5, 4 }, { 5, 4 }, { 5, 4 },
|
||||
{ 6, 3 }, { 5, 5 }, { 5, 5 }, { 5, 6 }, { 4, 8 }, { 6, 5 }, { 6, 5 }, { 6, 5 },
|
||||
{ 6, 6 }, { 6, 6 }, { 6, 6 }, { 6, 7 }, { 5, 9 }, { 7, 6 }, { 7, 6 }, { 8, 4 },
|
||||
{ 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 8 }, { 7, 8 }, { 7, 8 }, { 7, 9 }, { 8, 7 },
|
||||
{ 8, 7 }, { 9, 6 }, { 8, 8 }, { 8, 8 }, { 8, 9 }, { 8, 9 }, { 9, 8 }, { 9, 8 },
|
||||
{ 9, 8 }, { 10, 7 }, { 9, 9 }, { 9, 9 }, { 9, 10 }, { 8, 12 }, { 10, 9 }, { 10, 9 },
|
||||
{ 10, 9 }, { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 11 }, { 9, 13 }, { 11, 10 }, { 11, 10 },
|
||||
{ 12, 8 }, { 11, 11 }, { 11, 11 }, { 11, 11 }, { 11, 12 }, { 11, 12 }, { 11, 12 }, { 11, 13 },
|
||||
{ 12, 11 }, { 12, 11 }, { 13, 10 }, { 12, 12 }, { 12, 12 }, { 12, 13 }, { 12, 13 }, { 13, 12 },
|
||||
{ 13, 12 }, { 13, 12 }, { 14, 11 }, { 13, 13 }, { 13, 13 }, { 13, 14 }, { 12, 16 }, { 14, 13 },
|
||||
{ 14, 13 }, { 14, 13 }, { 14, 14 }, { 14, 14 }, { 14, 14 }, { 14, 15 }, { 13, 17 }, { 15, 14 },
|
||||
{ 15, 14 }, { 16, 12 }, { 15, 15 }, { 15, 15 }, { 15, 15 }, { 15, 16 }, { 15, 16 }, { 15, 16 },
|
||||
{ 15, 17 }, { 16, 15 }, { 16, 15 }, { 17, 14 }, { 16, 16 }, { 16, 16 }, { 16, 17 }, { 16, 17 },
|
||||
{ 17, 16 }, { 17, 16 }, { 17, 16 }, { 18, 15 }, { 17, 17 }, { 17, 17 }, { 17, 18 }, { 16, 20 },
|
||||
{ 18, 17 }, { 18, 17 }, { 18, 17 }, { 18, 18 }, { 18, 18 }, { 18, 18 }, { 18, 19 }, { 17, 21 },
|
||||
{ 19, 18 }, { 19, 18 }, { 20, 16 }, { 19, 19 }, { 19, 19 }, { 19, 19 }, { 19, 20 }, { 19, 20 },
|
||||
{ 19, 20 }, { 19, 21 }, { 20, 19 }, { 20, 19 }, { 21, 18 }, { 20, 20 }, { 20, 20 }, { 20, 21 },
|
||||
{ 20, 21 }, { 21, 20 }, { 21, 20 }, { 21, 20 }, { 22, 19 }, { 21, 21 }, { 21, 21 }, { 21, 22 },
|
||||
{ 20, 24 }, { 22, 21 }, { 22, 21 }, { 22, 21 }, { 22, 22 }, { 22, 22 }, { 22, 22 }, { 22, 23 },
|
||||
{ 21, 25 }, { 23, 22 }, { 23, 22 }, { 24, 20 }, { 23, 23 }, { 23, 23 }, { 23, 23 }, { 23, 24 },
|
||||
{ 23, 24 }, { 23, 24 }, { 23, 25 }, { 24, 23 }, { 24, 23 }, { 25, 22 }, { 24, 24 }, { 24, 24 },
|
||||
{ 24, 25 }, { 24, 25 }, { 25, 24 }, { 25, 24 }, { 25, 24 }, { 26, 23 }, { 25, 25 }, { 25, 25 },
|
||||
{ 25, 26 }, { 24, 28 }, { 26, 25 }, { 26, 25 }, { 26, 25 }, { 26, 26 }, { 26, 26 }, { 26, 26 },
|
||||
{ 26, 27 }, { 25, 29 }, { 27, 26 }, { 27, 26 }, { 28, 24 }, { 27, 27 }, { 27, 27 }, { 27, 27 },
|
||||
{ 27, 28 }, { 27, 28 }, { 27, 28 }, { 27, 29 }, { 28, 27 }, { 28, 27 }, { 29, 26 }, { 28, 28 },
|
||||
{ 28, 28 }, { 28, 29 }, { 28, 29 }, { 29, 28 }, { 29, 28 }, { 29, 28 }, { 30, 27 }, { 29, 29 },
|
||||
{ 29, 29 }, { 29, 30 }, { 29, 30 }, { 30, 29 }, { 30, 29 }, { 30, 29 }, { 30, 30 }, { 30, 30 },
|
||||
{ 30, 30 }, { 30, 31 }, { 30, 31 }, { 31, 30 }, { 31, 30 }, { 31, 30 }, { 31, 31 }, { 31, 31 },
|
||||
};
|
||||
static const unsigned char stb__OMatch6[256][2] = {
|
||||
{ 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 }, { 1, 1 }, { 1, 2 }, { 2, 1 }, { 2, 2 },
|
||||
{ 2, 2 }, { 2, 3 }, { 3, 2 }, { 3, 3 }, { 3, 3 }, { 3, 4 }, { 4, 3 }, { 4, 4 },
|
||||
{ 4, 4 }, { 4, 5 }, { 5, 4 }, { 5, 5 }, { 5, 5 }, { 5, 6 }, { 6, 5 }, { 6, 6 },
|
||||
{ 6, 6 }, { 6, 7 }, { 7, 6 }, { 7, 7 }, { 7, 7 }, { 7, 8 }, { 8, 7 }, { 8, 8 },
|
||||
{ 8, 8 }, { 8, 9 }, { 9, 8 }, { 9, 9 }, { 9, 9 }, { 9, 10 }, { 10, 9 }, { 10, 10 },
|
||||
{ 10, 10 }, { 10, 11 }, { 11, 10 }, { 8, 16 }, { 11, 11 }, { 11, 12 }, { 12, 11 }, { 9, 17 },
|
||||
{ 12, 12 }, { 12, 13 }, { 13, 12 }, { 11, 16 }, { 13, 13 }, { 13, 14 }, { 14, 13 }, { 12, 17 },
|
||||
{ 14, 14 }, { 14, 15 }, { 15, 14 }, { 14, 16 }, { 15, 15 }, { 15, 16 }, { 16, 14 }, { 16, 15 },
|
||||
{ 17, 14 }, { 16, 16 }, { 16, 17 }, { 17, 16 }, { 18, 15 }, { 17, 17 }, { 17, 18 }, { 18, 17 },
|
||||
{ 20, 14 }, { 18, 18 }, { 18, 19 }, { 19, 18 }, { 21, 15 }, { 19, 19 }, { 19, 20 }, { 20, 19 },
|
||||
{ 20, 20 }, { 20, 20 }, { 20, 21 }, { 21, 20 }, { 21, 21 }, { 21, 21 }, { 21, 22 }, { 22, 21 },
|
||||
{ 22, 22 }, { 22, 22 }, { 22, 23 }, { 23, 22 }, { 23, 23 }, { 23, 23 }, { 23, 24 }, { 24, 23 },
|
||||
{ 24, 24 }, { 24, 24 }, { 24, 25 }, { 25, 24 }, { 25, 25 }, { 25, 25 }, { 25, 26 }, { 26, 25 },
|
||||
{ 26, 26 }, { 26, 26 }, { 26, 27 }, { 27, 26 }, { 24, 32 }, { 27, 27 }, { 27, 28 }, { 28, 27 },
|
||||
{ 25, 33 }, { 28, 28 }, { 28, 29 }, { 29, 28 }, { 27, 32 }, { 29, 29 }, { 29, 30 }, { 30, 29 },
|
||||
{ 28, 33 }, { 30, 30 }, { 30, 31 }, { 31, 30 }, { 30, 32 }, { 31, 31 }, { 31, 32 }, { 32, 30 },
|
||||
{ 32, 31 }, { 33, 30 }, { 32, 32 }, { 32, 33 }, { 33, 32 }, { 34, 31 }, { 33, 33 }, { 33, 34 },
|
||||
{ 34, 33 }, { 36, 30 }, { 34, 34 }, { 34, 35 }, { 35, 34 }, { 37, 31 }, { 35, 35 }, { 35, 36 },
|
||||
{ 36, 35 }, { 36, 36 }, { 36, 36 }, { 36, 37 }, { 37, 36 }, { 37, 37 }, { 37, 37 }, { 37, 38 },
|
||||
{ 38, 37 }, { 38, 38 }, { 38, 38 }, { 38, 39 }, { 39, 38 }, { 39, 39 }, { 39, 39 }, { 39, 40 },
|
||||
{ 40, 39 }, { 40, 40 }, { 40, 40 }, { 40, 41 }, { 41, 40 }, { 41, 41 }, { 41, 41 }, { 41, 42 },
|
||||
{ 42, 41 }, { 42, 42 }, { 42, 42 }, { 42, 43 }, { 43, 42 }, { 40, 48 }, { 43, 43 }, { 43, 44 },
|
||||
{ 44, 43 }, { 41, 49 }, { 44, 44 }, { 44, 45 }, { 45, 44 }, { 43, 48 }, { 45, 45 }, { 45, 46 },
|
||||
{ 46, 45 }, { 44, 49 }, { 46, 46 }, { 46, 47 }, { 47, 46 }, { 46, 48 }, { 47, 47 }, { 47, 48 },
|
||||
{ 48, 46 }, { 48, 47 }, { 49, 46 }, { 48, 48 }, { 48, 49 }, { 49, 48 }, { 50, 47 }, { 49, 49 },
|
||||
{ 49, 50 }, { 50, 49 }, { 52, 46 }, { 50, 50 }, { 50, 51 }, { 51, 50 }, { 53, 47 }, { 51, 51 },
|
||||
{ 51, 52 }, { 52, 51 }, { 52, 52 }, { 52, 52 }, { 52, 53 }, { 53, 52 }, { 53, 53 }, { 53, 53 },
|
||||
{ 53, 54 }, { 54, 53 }, { 54, 54 }, { 54, 54 }, { 54, 55 }, { 55, 54 }, { 55, 55 }, { 55, 55 },
|
||||
{ 55, 56 }, { 56, 55 }, { 56, 56 }, { 56, 56 }, { 56, 57 }, { 57, 56 }, { 57, 57 }, { 57, 57 },
|
||||
{ 57, 58 }, { 58, 57 }, { 58, 58 }, { 58, 58 }, { 58, 59 }, { 59, 58 }, { 59, 59 }, { 59, 59 },
|
||||
{ 59, 60 }, { 60, 59 }, { 60, 60 }, { 60, 60 }, { 60, 61 }, { 61, 60 }, { 61, 61 }, { 61, 61 },
|
||||
{ 61, 62 }, { 62, 61 }, { 62, 62 }, { 62, 62 }, { 62, 63 }, { 63, 62 }, { 63, 63 }, { 63, 63 },
|
||||
};
|
||||
|
||||
static int stb__Mul8Bit(int a, int b)
|
||||
{
|
||||
int t = a*b + 128;
|
||||
return (t + (t >> 8)) >> 8;
|
||||
}
|
||||
|
||||
static void stb__From16Bit(unsigned char *out, unsigned short v)
|
||||
{
|
||||
int rv = (v & 0xf800) >> 11;
|
||||
int gv = (v & 0x07e0) >> 5;
|
||||
int bv = (v & 0x001f) >> 0;
|
||||
|
||||
// expand to 8 bits via bit replication
|
||||
out[0] = (rv * 33) >> 2;
|
||||
out[1] = (gv * 65) >> 4;
|
||||
out[2] = (bv * 33) >> 2;
|
||||
out[3] = 0;
|
||||
}
|
||||
|
||||
static unsigned short stb__As16Bit(int r, int g, int b)
|
||||
{
|
||||
return (unsigned short)((stb__Mul8Bit(r,31) << 11) + (stb__Mul8Bit(g,63) << 5) + stb__Mul8Bit(b,31));
|
||||
}
|
||||
|
||||
// linear interpolation at 1/3 point between a and b, using desired rounding type
|
||||
static int stb__Lerp13(int a, int b)
|
||||
{
|
||||
#ifdef STB_DXT_USE_ROUNDING_BIAS
|
||||
// with rounding bias
|
||||
return a + stb__Mul8Bit(b-a, 0x55);
|
||||
#else
|
||||
// without rounding bias
|
||||
// replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really need every ounce of speed.
|
||||
return (2*a + b) / 3;
|
||||
#endif
|
||||
}
|
||||
|
||||
// lerp RGB color
|
||||
static void stb__Lerp13RGB(unsigned char *out, unsigned char *p1, unsigned char *p2)
|
||||
{
|
||||
out[0] = (unsigned char)stb__Lerp13(p1[0], p2[0]);
|
||||
out[1] = (unsigned char)stb__Lerp13(p1[1], p2[1]);
|
||||
out[2] = (unsigned char)stb__Lerp13(p1[2], p2[2]);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static void stb__EvalColors(unsigned char *color,unsigned short c0,unsigned short c1)
|
||||
{
|
||||
stb__From16Bit(color+ 0, c0);
|
||||
stb__From16Bit(color+ 4, c1);
|
||||
stb__Lerp13RGB(color+ 8, color+0, color+4);
|
||||
stb__Lerp13RGB(color+12, color+4, color+0);
|
||||
}
|
||||
|
||||
// The color matching function
|
||||
static unsigned int stb__MatchColorsBlock(unsigned char *block, unsigned char *color)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
int dirr = color[0*4+0] - color[1*4+0];
|
||||
int dirg = color[0*4+1] - color[1*4+1];
|
||||
int dirb = color[0*4+2] - color[1*4+2];
|
||||
int dots[16];
|
||||
int stops[4];
|
||||
int i;
|
||||
int c0Point, halfPoint, c3Point;
|
||||
|
||||
for(i=0;i<16;i++)
|
||||
dots[i] = block[i*4+0]*dirr + block[i*4+1]*dirg + block[i*4+2]*dirb;
|
||||
|
||||
for(i=0;i<4;i++)
|
||||
stops[i] = color[i*4+0]*dirr + color[i*4+1]*dirg + color[i*4+2]*dirb;
|
||||
|
||||
// think of the colors as arranged on a line; project point onto that line, then choose
|
||||
// next color out of available ones. we compute the crossover points for "best color in top
|
||||
// half"/"best in bottom half" and then the same inside that subinterval.
|
||||
//
|
||||
// relying on this 1d approximation isn't always optimal in terms of euclidean distance,
|
||||
// but it's very close and a lot faster.
|
||||
// http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html
|
||||
|
||||
c0Point = (stops[1] + stops[3]);
|
||||
halfPoint = (stops[3] + stops[2]);
|
||||
c3Point = (stops[2] + stops[0]);
|
||||
|
||||
for (i=15;i>=0;i--) {
|
||||
int dot = dots[i]*2;
|
||||
mask <<= 2;
|
||||
|
||||
if(dot < halfPoint)
|
||||
mask |= (dot < c0Point) ? 1 : 3;
|
||||
else
|
||||
mask |= (dot < c3Point) ? 2 : 0;
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
// The color optimization function. (Clever code, part 1)
|
||||
static void stb__OptimizeColorsBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16)
|
||||
{
|
||||
int mind,maxd;
|
||||
unsigned char *minp, *maxp;
|
||||
double magn;
|
||||
int v_r,v_g,v_b;
|
||||
static const int nIterPower = 4;
|
||||
float covf[6],vfr,vfg,vfb;
|
||||
|
||||
// determine color distribution
|
||||
int cov[6];
|
||||
int mu[3],min[3],max[3];
|
||||
int ch,i,iter;
|
||||
|
||||
for(ch=0;ch<3;ch++)
|
||||
{
|
||||
const unsigned char *bp = ((const unsigned char *) block) + ch;
|
||||
int muv,minv,maxv;
|
||||
|
||||
muv = minv = maxv = bp[0];
|
||||
for(i=4;i<64;i+=4)
|
||||
{
|
||||
muv += bp[i];
|
||||
if (bp[i] < minv) minv = bp[i];
|
||||
else if (bp[i] > maxv) maxv = bp[i];
|
||||
}
|
||||
|
||||
mu[ch] = (muv + 8) >> 4;
|
||||
min[ch] = minv;
|
||||
max[ch] = maxv;
|
||||
}
|
||||
|
||||
// determine covariance matrix
|
||||
for (i=0;i<6;i++)
|
||||
cov[i] = 0;
|
||||
|
||||
for (i=0;i<16;i++)
|
||||
{
|
||||
int r = block[i*4+0] - mu[0];
|
||||
int g = block[i*4+1] - mu[1];
|
||||
int b = block[i*4+2] - mu[2];
|
||||
|
||||
cov[0] += r*r;
|
||||
cov[1] += r*g;
|
||||
cov[2] += r*b;
|
||||
cov[3] += g*g;
|
||||
cov[4] += g*b;
|
||||
cov[5] += b*b;
|
||||
}
|
||||
|
||||
// convert covariance matrix to float, find principal axis via power iter
|
||||
for(i=0;i<6;i++)
|
||||
covf[i] = cov[i] / 255.0f;
|
||||
|
||||
vfr = (float) (max[0] - min[0]);
|
||||
vfg = (float) (max[1] - min[1]);
|
||||
vfb = (float) (max[2] - min[2]);
|
||||
|
||||
for(iter=0;iter<nIterPower;iter++)
|
||||
{
|
||||
float r = vfr*covf[0] + vfg*covf[1] + vfb*covf[2];
|
||||
float g = vfr*covf[1] + vfg*covf[3] + vfb*covf[4];
|
||||
float b = vfr*covf[2] + vfg*covf[4] + vfb*covf[5];
|
||||
|
||||
vfr = r;
|
||||
vfg = g;
|
||||
vfb = b;
|
||||
}
|
||||
|
||||
magn = STBD_FABS(vfr);
|
||||
if (STBD_FABS(vfg) > magn) magn = STBD_FABS(vfg);
|
||||
if (STBD_FABS(vfb) > magn) magn = STBD_FABS(vfb);
|
||||
|
||||
if(magn < 4.0f) { // too small, default to luminance
|
||||
v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000.
|
||||
v_g = 587;
|
||||
v_b = 114;
|
||||
} else {
|
||||
magn = 512.0 / magn;
|
||||
v_r = (int) (vfr * magn);
|
||||
v_g = (int) (vfg * magn);
|
||||
v_b = (int) (vfb * magn);
|
||||
}
|
||||
|
||||
minp = maxp = block;
|
||||
mind = maxd = block[0]*v_r + block[1]*v_g + block[2]*v_b;
|
||||
// Pick colors at extreme points
|
||||
for(i=1;i<16;i++)
|
||||
{
|
||||
int dot = block[i*4+0]*v_r + block[i*4+1]*v_g + block[i*4+2]*v_b;
|
||||
|
||||
if (dot < mind) {
|
||||
mind = dot;
|
||||
minp = block+i*4;
|
||||
}
|
||||
|
||||
if (dot > maxd) {
|
||||
maxd = dot;
|
||||
maxp = block+i*4;
|
||||
}
|
||||
}
|
||||
|
||||
*pmax16 = stb__As16Bit(maxp[0],maxp[1],maxp[2]);
|
||||
*pmin16 = stb__As16Bit(minp[0],minp[1],minp[2]);
|
||||
}
|
||||
|
||||
static const float stb__midpoints5[32] = {
|
||||
0.015686f, 0.047059f, 0.078431f, 0.111765f, 0.145098f, 0.176471f, 0.207843f, 0.241176f, 0.274510f, 0.305882f, 0.337255f, 0.370588f, 0.403922f, 0.435294f, 0.466667f, 0.5f,
|
||||
0.533333f, 0.564706f, 0.596078f, 0.629412f, 0.662745f, 0.694118f, 0.725490f, 0.758824f, 0.792157f, 0.823529f, 0.854902f, 0.888235f, 0.921569f, 0.952941f, 0.984314f, 1.0f
|
||||
};
|
||||
|
||||
static const float stb__midpoints6[64] = {
|
||||
0.007843f, 0.023529f, 0.039216f, 0.054902f, 0.070588f, 0.086275f, 0.101961f, 0.117647f, 0.133333f, 0.149020f, 0.164706f, 0.180392f, 0.196078f, 0.211765f, 0.227451f, 0.245098f,
|
||||
0.262745f, 0.278431f, 0.294118f, 0.309804f, 0.325490f, 0.341176f, 0.356863f, 0.372549f, 0.388235f, 0.403922f, 0.419608f, 0.435294f, 0.450980f, 0.466667f, 0.482353f, 0.500000f,
|
||||
0.517647f, 0.533333f, 0.549020f, 0.564706f, 0.580392f, 0.596078f, 0.611765f, 0.627451f, 0.643137f, 0.658824f, 0.674510f, 0.690196f, 0.705882f, 0.721569f, 0.737255f, 0.754902f,
|
||||
0.772549f, 0.788235f, 0.803922f, 0.819608f, 0.835294f, 0.850980f, 0.866667f, 0.882353f, 0.898039f, 0.913725f, 0.929412f, 0.945098f, 0.960784f, 0.976471f, 0.992157f, 1.0f
|
||||
};
|
||||
|
||||
static unsigned short stb__Quantize5(float x)
|
||||
{
|
||||
unsigned short q;
|
||||
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
|
||||
q = (unsigned short)(x * 31);
|
||||
q += (x > stb__midpoints5[q]);
|
||||
return q;
|
||||
}
|
||||
|
||||
static unsigned short stb__Quantize6(float x)
|
||||
{
|
||||
unsigned short q;
|
||||
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
|
||||
q = (unsigned short)(x * 63);
|
||||
q += (x > stb__midpoints6[q]);
|
||||
return q;
|
||||
}
|
||||
|
||||
// The refinement function. (Clever code, part 2)
|
||||
// Tries to optimize colors to suit block contents better.
|
||||
// (By solving a least squares system via normal equations+Cramer's rule)
|
||||
static int stb__RefineBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16, unsigned int mask)
|
||||
{
|
||||
static const int w1Tab[4] = { 3,0,2,1 };
|
||||
static const int prods[4] = { 0x090000,0x000900,0x040102,0x010402 };
|
||||
// ^some magic to save a lot of multiplies in the accumulating loop...
|
||||
// (precomputed products of weights for least squares system, accumulated inside one 32-bit register)
|
||||
|
||||
float f;
|
||||
unsigned short oldMin, oldMax, min16, max16;
|
||||
int i, akku = 0, xx,xy,yy;
|
||||
int At1_r,At1_g,At1_b;
|
||||
int At2_r,At2_g,At2_b;
|
||||
unsigned int cm = mask;
|
||||
|
||||
oldMin = *pmin16;
|
||||
oldMax = *pmax16;
|
||||
|
||||
if((mask ^ (mask<<2)) < 4) // all pixels have the same index?
|
||||
{
|
||||
// yes, linear system would be singular; solve using optimal
|
||||
// single-color match on average color
|
||||
int r = 8, g = 8, b = 8;
|
||||
for (i=0;i<16;++i) {
|
||||
r += block[i*4+0];
|
||||
g += block[i*4+1];
|
||||
b += block[i*4+2];
|
||||
}
|
||||
|
||||
r >>= 4; g >>= 4; b >>= 4;
|
||||
|
||||
max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
|
||||
min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
|
||||
} else {
|
||||
At1_r = At1_g = At1_b = 0;
|
||||
At2_r = At2_g = At2_b = 0;
|
||||
for (i=0;i<16;++i,cm>>=2) {
|
||||
int step = cm&3;
|
||||
int w1 = w1Tab[step];
|
||||
int r = block[i*4+0];
|
||||
int g = block[i*4+1];
|
||||
int b = block[i*4+2];
|
||||
|
||||
akku += prods[step];
|
||||
At1_r += w1*r;
|
||||
At1_g += w1*g;
|
||||
At1_b += w1*b;
|
||||
At2_r += r;
|
||||
At2_g += g;
|
||||
At2_b += b;
|
||||
}
|
||||
|
||||
At2_r = 3*At2_r - At1_r;
|
||||
At2_g = 3*At2_g - At1_g;
|
||||
At2_b = 3*At2_b - At1_b;
|
||||
|
||||
// extract solutions and decide solvability
|
||||
xx = akku >> 16;
|
||||
yy = (akku >> 8) & 0xff;
|
||||
xy = (akku >> 0) & 0xff;
|
||||
|
||||
f = 3.0f / 255.0f / (xx*yy - xy*xy);
|
||||
|
||||
max16 = stb__Quantize5((At1_r*yy - At2_r * xy) * f) << 11;
|
||||
max16 |= stb__Quantize6((At1_g*yy - At2_g * xy) * f) << 5;
|
||||
max16 |= stb__Quantize5((At1_b*yy - At2_b * xy) * f) << 0;
|
||||
|
||||
min16 = stb__Quantize5((At2_r*xx - At1_r * xy) * f) << 11;
|
||||
min16 |= stb__Quantize6((At2_g*xx - At1_g * xy) * f) << 5;
|
||||
min16 |= stb__Quantize5((At2_b*xx - At1_b * xy) * f) << 0;
|
||||
}
|
||||
|
||||
*pmin16 = min16;
|
||||
*pmax16 = max16;
|
||||
return oldMin != min16 || oldMax != max16;
|
||||
}
|
||||
|
||||
// Color block compression
|
||||
static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block, int mode)
|
||||
{
|
||||
unsigned int mask;
|
||||
int i;
|
||||
int refinecount;
|
||||
unsigned short max16, min16;
|
||||
unsigned char color[4*4];
|
||||
|
||||
refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1;
|
||||
|
||||
// check if block is constant
|
||||
for (i=1;i<16;i++)
|
||||
if (((unsigned int *) block)[i] != ((unsigned int *) block)[0])
|
||||
break;
|
||||
|
||||
if(i == 16) { // constant color
|
||||
int r = block[0], g = block[1], b = block[2];
|
||||
mask = 0xaaaaaaaa;
|
||||
max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
|
||||
min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
|
||||
} else {
|
||||
// first step: PCA+map along principal axis
|
||||
stb__OptimizeColorsBlock(block,&max16,&min16);
|
||||
if (max16 != min16) {
|
||||
stb__EvalColors(color,max16,min16);
|
||||
mask = stb__MatchColorsBlock(block,color);
|
||||
} else
|
||||
mask = 0;
|
||||
|
||||
// third step: refine (multiple times if requested)
|
||||
for (i=0;i<refinecount;i++) {
|
||||
unsigned int lastmask = mask;
|
||||
|
||||
if (stb__RefineBlock(block,&max16,&min16,mask)) {
|
||||
if (max16 != min16) {
|
||||
stb__EvalColors(color,max16,min16);
|
||||
mask = stb__MatchColorsBlock(block,color);
|
||||
} else {
|
||||
mask = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(mask == lastmask)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// write the color block
|
||||
if(max16 < min16)
|
||||
{
|
||||
unsigned short t = min16;
|
||||
min16 = max16;
|
||||
max16 = t;
|
||||
mask ^= 0x55555555;
|
||||
}
|
||||
|
||||
dest[0] = (unsigned char) (max16);
|
||||
dest[1] = (unsigned char) (max16 >> 8);
|
||||
dest[2] = (unsigned char) (min16);
|
||||
dest[3] = (unsigned char) (min16 >> 8);
|
||||
dest[4] = (unsigned char) (mask);
|
||||
dest[5] = (unsigned char) (mask >> 8);
|
||||
dest[6] = (unsigned char) (mask >> 16);
|
||||
dest[7] = (unsigned char) (mask >> 24);
|
||||
}
|
||||
|
||||
// Alpha block compression (this is easy for a change)
|
||||
static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src, int stride)
|
||||
{
|
||||
int i,dist,bias,dist4,dist2,bits,mask;
|
||||
|
||||
// find min/max color
|
||||
int mn,mx;
|
||||
mn = mx = src[0];
|
||||
|
||||
for (i=1;i<16;i++)
|
||||
{
|
||||
if (src[i*stride] < mn) mn = src[i*stride];
|
||||
else if (src[i*stride] > mx) mx = src[i*stride];
|
||||
}
|
||||
|
||||
// encode them
|
||||
dest[0] = (unsigned char)mx;
|
||||
dest[1] = (unsigned char)mn;
|
||||
dest += 2;
|
||||
|
||||
// determine bias and emit color indices
|
||||
// given the choice of mx/mn, these indices are optimal:
|
||||
// http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/
|
||||
dist = mx-mn;
|
||||
dist4 = dist*4;
|
||||
dist2 = dist*2;
|
||||
bias = (dist < 8) ? (dist - 1) : (dist/2 + 2);
|
||||
bias -= mn * 7;
|
||||
bits = 0,mask=0;
|
||||
|
||||
for (i=0;i<16;i++) {
|
||||
int a = src[i*stride]*7 + bias;
|
||||
int ind,t;
|
||||
|
||||
// select index. this is a "linear scale" lerp factor between 0 (val=min) and 7 (val=max).
|
||||
t = (a >= dist4) ? -1 : 0; ind = t & 4; a -= dist4 & t;
|
||||
t = (a >= dist2) ? -1 : 0; ind += t & 2; a -= dist2 & t;
|
||||
ind += (a >= dist);
|
||||
|
||||
// turn linear scale into DXT index (0/1 are extremal pts)
|
||||
ind = -ind & 7;
|
||||
ind ^= (2 > ind);
|
||||
|
||||
// write index
|
||||
mask |= ind << bits;
|
||||
if((bits += 3) >= 8) {
|
||||
*dest++ = (unsigned char)mask;
|
||||
mask >>= 8;
|
||||
bits -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode)
|
||||
{
|
||||
unsigned char data[16][4];
|
||||
if (alpha) {
|
||||
int i;
|
||||
stb__CompressAlphaBlock(dest,(unsigned char*) src+3, 4);
|
||||
dest += 8;
|
||||
// make a new copy of the data in which alpha is opaque,
|
||||
// because code uses a fast test for color constancy
|
||||
memcpy(data, src, 4*16);
|
||||
for (i=0; i < 16; ++i)
|
||||
data[i][3] = 255;
|
||||
src = &data[0][0];
|
||||
}
|
||||
|
||||
stb__CompressColorBlock(dest,(unsigned char*) src,mode);
|
||||
}
|
||||
|
||||
void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src)
|
||||
{
|
||||
stb__CompressAlphaBlock(dest,(unsigned char*) src, 1);
|
||||
}
|
||||
|
||||
void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src)
|
||||
{
|
||||
stb__CompressAlphaBlock(dest,(unsigned char*) src,2);
|
||||
stb__CompressAlphaBlock(dest + 8,(unsigned char*) src+1,2);
|
||||
}
|
||||
#endif // STB_DXT_IMPLEMENTATION
|
||||
|
||||
// Compile with STB_DXT_IMPLEMENTATION and STB_DXT_GENERATE_TABLES
|
||||
// defined to generate the tables above.
|
||||
#ifdef STB_DXT_GENERATE_TABLES
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int i, j;
|
||||
const char *omatch_names[] = { "stb__OMatch5", "stb__OMatch6" };
|
||||
int dequant_mults[2] = { 33*4, 65 }; // .4 fixed-point dequant multipliers
|
||||
|
||||
// optimal endpoint tables
|
||||
for (i = 0; i < 2; ++i) {
|
||||
int dequant = dequant_mults[i];
|
||||
int size = i ? 64 : 32;
|
||||
printf("static const unsigned char %s[256][2] = {\n", omatch_names[i]);
|
||||
for (int j = 0; j < 256; ++j) {
|
||||
int mn, mx;
|
||||
int best_mn = 0, best_mx = 0;
|
||||
int best_err = 256 * 100;
|
||||
for (mn=0;mn<size;mn++) {
|
||||
for (mx=0;mx<size;mx++) {
|
||||
int mine = (mn * dequant) >> 4;
|
||||
int maxe = (mx * dequant) >> 4;
|
||||
int err = abs(stb__Lerp13(maxe, mine) - j) * 100;
|
||||
|
||||
// DX10 spec says that interpolation must be within 3% of "correct" result,
|
||||
// add this as error term. Normally we'd expect a random distribution of
|
||||
// +-1.5% error, but nowhere in the spec does it say that the error has to be
|
||||
// unbiased - better safe than sorry.
|
||||
err += abs(maxe - mine) * 3;
|
||||
|
||||
if(err < best_err) {
|
||||
best_mn = mn;
|
||||
best_mx = mx;
|
||||
best_err = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((j % 8) == 0) printf(" "); // 2 spaces, third is done below
|
||||
printf(" { %2d, %2d },", best_mx, best_mn);
|
||||
if ((j % 8) == 7) printf("\n");
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
305
thirdparty/stb/stb_easy_font.h
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
// stb_easy_font.h - v1.1 - bitmap font for 3D rendering - public domain
|
||||
// Sean Barrett, Feb 2015
|
||||
//
|
||||
// Easy-to-deploy,
|
||||
// reasonably compact,
|
||||
// extremely inefficient performance-wise,
|
||||
// crappy-looking,
|
||||
// ASCII-only,
|
||||
// bitmap font for use in 3D APIs.
|
||||
//
|
||||
// Intended for when you just want to get some text displaying
|
||||
// in a 3D app as quickly as possible.
|
||||
//
|
||||
// Doesn't use any textures, instead builds characters out of quads.
|
||||
//
|
||||
// DOCUMENTATION:
|
||||
//
|
||||
// int stb_easy_font_width(char *text)
|
||||
// int stb_easy_font_height(char *text)
|
||||
//
|
||||
// Takes a string and returns the horizontal size and the
|
||||
// vertical size (which can vary if 'text' has newlines).
|
||||
//
|
||||
// int stb_easy_font_print(float x, float y,
|
||||
// char *text, unsigned char color[4],
|
||||
// void *vertex_buffer, int vbuf_size)
|
||||
//
|
||||
// Takes a string (which can contain '\n') and fills out a
|
||||
// vertex buffer with renderable data to draw the string.
|
||||
// Output data assumes increasing x is rightwards, increasing y
|
||||
// is downwards.
|
||||
//
|
||||
// The vertex data is divided into quads, i.e. there are four
|
||||
// vertices in the vertex buffer for each quad.
|
||||
//
|
||||
// The vertices are stored in an interleaved format:
|
||||
//
|
||||
// x:float
|
||||
// y:float
|
||||
// z:float
|
||||
// color:uint8[4]
|
||||
//
|
||||
// You can ignore z and color if you get them from elsewhere
|
||||
// This format was chosen in the hopes it would make it
|
||||
// easier for you to reuse existing vertex-buffer-drawing code.
|
||||
//
|
||||
// If you pass in NULL for color, it becomes 255,255,255,255.
|
||||
//
|
||||
// Returns the number of quads.
|
||||
//
|
||||
// If the buffer isn't large enough, it will truncate.
|
||||
// Expect it to use an average of ~270 bytes per character.
|
||||
//
|
||||
// If your API doesn't draw quads, build a reusable index
|
||||
// list that allows you to render quads as indexed triangles.
|
||||
//
|
||||
// void stb_easy_font_spacing(float spacing)
|
||||
//
|
||||
// Use positive values to expand the space between characters,
|
||||
// and small negative values (no smaller than -1.5) to contract
|
||||
// the space between characters.
|
||||
//
|
||||
// E.g. spacing = 1 adds one "pixel" of spacing between the
|
||||
// characters. spacing = -1 is reasonable but feels a bit too
|
||||
// compact to me; -0.5 is a reasonable compromise as long as
|
||||
// you're scaling the font up.
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
//
|
||||
// VERSION HISTORY
|
||||
//
|
||||
// (2020-02-02) 1.1 make everything static so can compile it in more than one src file
|
||||
// (2017-01-15) 1.0 space character takes same space as numbers; fix bad spacing of 'f'
|
||||
// (2016-01-22) 0.7 width() supports multiline text; add height()
|
||||
// (2015-09-13) 0.6 #include <math.h>; updated license
|
||||
// (2015-02-01) 0.5 First release
|
||||
//
|
||||
// CONTRIBUTORS
|
||||
//
|
||||
// github:vassvik -- bug report
|
||||
// github:podsvirov -- fix multiple definition errors
|
||||
|
||||
#if 0
|
||||
// SAMPLE CODE:
|
||||
//
|
||||
// Here's sample code for old OpenGL; it's a lot more complicated
|
||||
// to make work on modern APIs, and that's your problem.
|
||||
//
|
||||
void print_string(float x, float y, char *text, float r, float g, float b)
|
||||
{
|
||||
static char buffer[99999]; // ~500 chars
|
||||
int num_quads;
|
||||
|
||||
num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer));
|
||||
|
||||
glColor3f(r,g,b);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, 16, buffer);
|
||||
glDrawArrays(GL_QUADS, 0, num_quads*4);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef INCLUDE_STB_EASY_FONT_H
|
||||
#define INCLUDE_STB_EASY_FONT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
static struct stb_easy_font_info_struct {
|
||||
unsigned char advance;
|
||||
unsigned char h_seg;
|
||||
unsigned char v_seg;
|
||||
} stb_easy_font_charinfo[96] = {
|
||||
{ 6, 0, 0 }, { 3, 0, 0 }, { 5, 1, 1 }, { 7, 1, 4 },
|
||||
{ 7, 3, 7 }, { 7, 6, 12 }, { 7, 8, 19 }, { 4, 16, 21 },
|
||||
{ 4, 17, 22 }, { 4, 19, 23 }, { 23, 21, 24 }, { 23, 22, 31 },
|
||||
{ 20, 23, 34 }, { 22, 23, 36 }, { 19, 24, 36 }, { 21, 25, 36 },
|
||||
{ 6, 25, 39 }, { 6, 27, 43 }, { 6, 28, 45 }, { 6, 30, 49 },
|
||||
{ 6, 33, 53 }, { 6, 34, 57 }, { 6, 40, 58 }, { 6, 46, 59 },
|
||||
{ 6, 47, 62 }, { 6, 55, 64 }, { 19, 57, 68 }, { 20, 59, 68 },
|
||||
{ 21, 61, 69 }, { 22, 66, 69 }, { 21, 68, 69 }, { 7, 73, 69 },
|
||||
{ 9, 75, 74 }, { 6, 78, 81 }, { 6, 80, 85 }, { 6, 83, 90 },
|
||||
{ 6, 85, 91 }, { 6, 87, 95 }, { 6, 90, 96 }, { 7, 92, 97 },
|
||||
{ 6, 96,102 }, { 5, 97,106 }, { 6, 99,107 }, { 6,100,110 },
|
||||
{ 6,100,115 }, { 7,101,116 }, { 6,101,121 }, { 6,101,125 },
|
||||
{ 6,102,129 }, { 7,103,133 }, { 6,104,140 }, { 6,105,145 },
|
||||
{ 7,107,149 }, { 6,108,151 }, { 7,109,155 }, { 7,109,160 },
|
||||
{ 7,109,165 }, { 7,118,167 }, { 6,118,172 }, { 4,120,176 },
|
||||
{ 6,122,177 }, { 4,122,181 }, { 23,124,182 }, { 22,129,182 },
|
||||
{ 4,130,182 }, { 22,131,183 }, { 6,133,187 }, { 22,135,191 },
|
||||
{ 6,137,192 }, { 22,139,196 }, { 6,144,197 }, { 22,147,198 },
|
||||
{ 6,150,202 }, { 19,151,206 }, { 21,152,207 }, { 6,155,209 },
|
||||
{ 3,160,210 }, { 23,160,211 }, { 22,164,216 }, { 22,165,220 },
|
||||
{ 22,167,224 }, { 22,169,228 }, { 21,171,232 }, { 21,173,233 },
|
||||
{ 5,178,233 }, { 22,179,234 }, { 23,180,238 }, { 23,180,243 },
|
||||
{ 23,180,248 }, { 22,189,248 }, { 22,191,252 }, { 5,196,252 },
|
||||
{ 3,203,252 }, { 5,203,253 }, { 22,210,253 }, { 0,214,253 },
|
||||
};
|
||||
|
||||
static unsigned char stb_easy_font_hseg[214] = {
|
||||
97,37,69,84,28,51,2,18,10,49,98,41,65,25,81,105,33,9,97,1,97,37,37,36,
|
||||
81,10,98,107,3,100,3,99,58,51,4,99,58,8,73,81,10,50,98,8,73,81,4,10,50,
|
||||
98,8,25,33,65,81,10,50,17,65,97,25,33,25,49,9,65,20,68,1,65,25,49,41,
|
||||
11,105,13,101,76,10,50,10,50,98,11,99,10,98,11,50,99,11,50,11,99,8,57,
|
||||
58,3,99,99,107,10,10,11,10,99,11,5,100,41,65,57,41,65,9,17,81,97,3,107,
|
||||
9,97,1,97,33,25,9,25,41,100,41,26,82,42,98,27,83,42,98,26,51,82,8,41,
|
||||
35,8,10,26,82,114,42,1,114,8,9,73,57,81,41,97,18,8,8,25,26,26,82,26,82,
|
||||
26,82,41,25,33,82,26,49,73,35,90,17,81,41,65,57,41,65,25,81,90,114,20,
|
||||
84,73,57,41,49,25,33,65,81,9,97,1,97,25,33,65,81,57,33,25,41,25,
|
||||
};
|
||||
|
||||
static unsigned char stb_easy_font_vseg[253] = {
|
||||
4,2,8,10,15,8,15,33,8,15,8,73,82,73,57,41,82,10,82,18,66,10,21,29,1,65,
|
||||
27,8,27,9,65,8,10,50,97,74,66,42,10,21,57,41,29,25,14,81,73,57,26,8,8,
|
||||
26,66,3,8,8,15,19,21,90,58,26,18,66,18,105,89,28,74,17,8,73,57,26,21,
|
||||
8,42,41,42,8,28,22,8,8,30,7,8,8,26,66,21,7,8,8,29,7,7,21,8,8,8,59,7,8,
|
||||
8,15,29,8,8,14,7,57,43,10,82,7,7,25,42,25,15,7,25,41,15,21,105,105,29,
|
||||
7,57,57,26,21,105,73,97,89,28,97,7,57,58,26,82,18,57,57,74,8,30,6,8,8,
|
||||
14,3,58,90,58,11,7,74,43,74,15,2,82,2,42,75,42,10,67,57,41,10,7,2,42,
|
||||
74,106,15,2,35,8,8,29,7,8,8,59,35,51,8,8,15,35,30,35,8,8,30,7,8,8,60,
|
||||
36,8,45,7,7,36,8,43,8,44,21,8,8,44,35,8,8,43,23,8,8,43,35,8,8,31,21,15,
|
||||
20,8,8,28,18,58,89,58,26,21,89,73,89,29,20,8,8,30,7,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char c[4];
|
||||
} stb_easy_font_color;
|
||||
|
||||
static int stb_easy_font_draw_segs(float x, float y, unsigned char *segs, int num_segs, int vertical, stb_easy_font_color c, char *vbuf, int vbuf_size, int offset)
|
||||
{
|
||||
int i,j;
|
||||
for (i=0; i < num_segs; ++i) {
|
||||
int len = segs[i] & 7;
|
||||
x += (float) ((segs[i] >> 3) & 1);
|
||||
if (len && offset+64 <= vbuf_size) {
|
||||
float y0 = y + (float) (segs[i]>>4);
|
||||
for (j=0; j < 4; ++j) {
|
||||
* (float *) (vbuf+offset+0) = x + (j==1 || j==2 ? (vertical ? 1 : len) : 0);
|
||||
* (float *) (vbuf+offset+4) = y0 + ( j >= 2 ? (vertical ? len : 1) : 0);
|
||||
* (float *) (vbuf+offset+8) = 0.f;
|
||||
* (stb_easy_font_color *) (vbuf+offset+12) = c;
|
||||
offset += 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static float stb_easy_font_spacing_val = 0;
|
||||
static void stb_easy_font_spacing(float spacing)
|
||||
{
|
||||
stb_easy_font_spacing_val = spacing;
|
||||
}
|
||||
|
||||
static int stb_easy_font_print(float x, float y, char *text, unsigned char color[4], void *vertex_buffer, int vbuf_size)
|
||||
{
|
||||
char *vbuf = (char *) vertex_buffer;
|
||||
float start_x = x;
|
||||
int offset = 0;
|
||||
|
||||
stb_easy_font_color c = { 255,255,255,255 }; // use structure copying to avoid needing depending on memcpy()
|
||||
if (color) { c.c[0] = color[0]; c.c[1] = color[1]; c.c[2] = color[2]; c.c[3] = color[3]; }
|
||||
|
||||
while (*text && offset < vbuf_size) {
|
||||
if (*text == '\n') {
|
||||
y += 12;
|
||||
x = start_x;
|
||||
} else {
|
||||
unsigned char advance = stb_easy_font_charinfo[*text-32].advance;
|
||||
float y_ch = advance & 16 ? y+1 : y;
|
||||
int h_seg, v_seg, num_h, num_v;
|
||||
h_seg = stb_easy_font_charinfo[*text-32 ].h_seg;
|
||||
v_seg = stb_easy_font_charinfo[*text-32 ].v_seg;
|
||||
num_h = stb_easy_font_charinfo[*text-32+1].h_seg - h_seg;
|
||||
num_v = stb_easy_font_charinfo[*text-32+1].v_seg - v_seg;
|
||||
offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_hseg[h_seg], num_h, 0, c, vbuf, vbuf_size, offset);
|
||||
offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_vseg[v_seg], num_v, 1, c, vbuf, vbuf_size, offset);
|
||||
x += advance & 15;
|
||||
x += stb_easy_font_spacing_val;
|
||||
}
|
||||
++text;
|
||||
}
|
||||
return (unsigned) offset/64;
|
||||
}
|
||||
|
||||
static int stb_easy_font_width(char *text)
|
||||
{
|
||||
float len = 0;
|
||||
float max_len = 0;
|
||||
while (*text) {
|
||||
if (*text == '\n') {
|
||||
if (len > max_len) max_len = len;
|
||||
len = 0;
|
||||
} else {
|
||||
len += stb_easy_font_charinfo[*text-32].advance & 15;
|
||||
len += stb_easy_font_spacing_val;
|
||||
}
|
||||
++text;
|
||||
}
|
||||
if (len > max_len) max_len = len;
|
||||
return (int) ceil(max_len);
|
||||
}
|
||||
|
||||
static int stb_easy_font_height(char *text)
|
||||
{
|
||||
float y = 0;
|
||||
int nonempty_line=0;
|
||||
while (*text) {
|
||||
if (*text == '\n') {
|
||||
y += 12;
|
||||
nonempty_line = 0;
|
||||
} else {
|
||||
nonempty_line = 1;
|
||||
}
|
||||
++text;
|
||||
}
|
||||
return (int) ceil(y + (nonempty_line ? 12 : 0));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
1221
thirdparty/stb/stb_herringbone_wang_tile.h
vendored
Normal file
680
thirdparty/stb/stb_hexwave.h
vendored
Normal file
@@ -0,0 +1,680 @@
|
||||
// stb_hexwave - v0.5 - public domain, initial release 2021-04-01
|
||||
//
|
||||
// A flexible anti-aliased (bandlimited) digital audio oscillator.
|
||||
//
|
||||
// This library generates waveforms of a variety of shapes made of
|
||||
// line segments. It does not do envelopes, LFO effects, etc.; it
|
||||
// merely tries to solve the problem of generating an artifact-free
|
||||
// morphable digital waveform with a variety of spectra, and leaves
|
||||
// it to the user to rescale the waveform and mix multiple voices, etc.
|
||||
//
|
||||
// Compiling:
|
||||
//
|
||||
// In one C/C++ file that #includes this file, do
|
||||
//
|
||||
// #define STB_HEXWAVE_IMPLEMENTATION
|
||||
// #include "stb_hexwave.h"
|
||||
//
|
||||
// Optionally, #define STB_HEXWAVE_STATIC before including
|
||||
// the header to cause the definitions to be private to the
|
||||
// implementation file (i.e. to be "static" instead of "extern").
|
||||
//
|
||||
// Notes:
|
||||
//
|
||||
// Optionally performs memory allocation during initialization,
|
||||
// never allocates otherwise.
|
||||
//
|
||||
// License:
|
||||
//
|
||||
// See end of file for license information.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// Initialization:
|
||||
//
|
||||
// hexwave_init(32,16,NULL); // read "header section" for alternatives
|
||||
//
|
||||
// Create oscillator:
|
||||
//
|
||||
// HexWave *osc = malloc(sizeof(*osc)); // or "new HexWave", or declare globally or on stack
|
||||
// hexwave_create(osc, reflect_flag, peak_time, half_height, zero_wait);
|
||||
// see "Waveform shapes" below for the meaning of these parameters
|
||||
//
|
||||
// Generate audio:
|
||||
//
|
||||
// hexwave_generate_samples(output, number_of_samples, osc, oscillator_freq)
|
||||
// where:
|
||||
// output is a buffer where the library will store floating point audio samples
|
||||
// number_of_samples is the number of audio samples to generate
|
||||
// osc is a pointer to a Hexwave
|
||||
// oscillator_freq is the frequency of the oscillator divided by the sample rate
|
||||
//
|
||||
// The output samples will continue from where the samples generated by the
|
||||
// previous hexwave_generate_samples() on this oscillator ended.
|
||||
//
|
||||
// Change oscillator waveform:
|
||||
//
|
||||
// hexwave_change(osc, reflect_flag, peak_time, half_height, zero_wait);
|
||||
// can call in between calls to hexwave_generate_samples
|
||||
//
|
||||
// Waveform shapes:
|
||||
//
|
||||
// All waveforms generated by hexwave are constructed from six line segments
|
||||
// characterized by 3 parameters.
|
||||
//
|
||||
// See demonstration: https://www.youtube.com/watch?v=hsUCrAsDN-M
|
||||
//
|
||||
// reflect=0 reflect=1
|
||||
//
|
||||
// 0-----P---1 0-----P---1 peak_time = P
|
||||
// . 1 . 1
|
||||
// /\_ : /\_ :
|
||||
// / \_ : / \_ :
|
||||
// / \.H / \.H half_height = H
|
||||
// / | : / | :
|
||||
// _____/ |_:___ _____/ | : _____
|
||||
// . : \ | . | : /
|
||||
// . : \ | . | : /
|
||||
// . : \ _/ . \_: /
|
||||
// . : \ _/ . :_ /
|
||||
// . -1 \/ . -1 \/
|
||||
// 0 - Z - - - - 1 0 - Z - - - - 1 zero_wait = Z
|
||||
//
|
||||
// Classic waveforms:
|
||||
// peak half zero
|
||||
// reflect time height wait
|
||||
// Sawtooth 1 0 0 0
|
||||
// Square 1 0 1 0
|
||||
// Triangle 1 0.5 0 0
|
||||
//
|
||||
// Some waveforms can be produced in multiple ways, which is useful when morphing
|
||||
// into other waveforms, and there are a few more notable shapes:
|
||||
//
|
||||
// peak half zero
|
||||
// reflect time height wait
|
||||
// Sawtooth 1 1 any 0
|
||||
// Sawtooth (8va) 1 0 -1 0
|
||||
// Triangle 1 0.5 0 0
|
||||
// Square 1 0 1 0
|
||||
// Square 0 0 1 0
|
||||
// Triangle 0 0.5 0 0
|
||||
// Triangle 0 0 -1 0
|
||||
// AlternatingSaw 0 0 0 0
|
||||
// AlternatingSaw 0 1 any 0
|
||||
// Stairs 0 0 1 0.5
|
||||
//
|
||||
// The "Sawtooth (8va)" waveform is identical to a sawtooth wave with 2x the
|
||||
// frequency, but when morphed with other values, it becomes an overtone of
|
||||
// the base frequency.
|
||||
//
|
||||
// Morphing waveforms:
|
||||
//
|
||||
// Sweeping peak_time morphs the waveform while producing various spectra.
|
||||
// Sweeping half_height effectively crossfades between two waveforms; useful, but less exciting.
|
||||
// Sweeping zero_wait produces a similar effect no matter the reset of the waveform,
|
||||
// a sort of high-pass/PWM effect where the wave becomes silent at zero_wait=1.
|
||||
//
|
||||
// You can trivially morph between any two waveforms from the above table
|
||||
// which only differ in one column.
|
||||
//
|
||||
// Crossfade between classic waveforms:
|
||||
// peak half zero
|
||||
// Start End reflect time height wait
|
||||
// ----- --- ------- ---- ------ ----
|
||||
// Triangle Square 0 0 -1..1 0
|
||||
// Saw Square 1 0 0..1 0
|
||||
// Triangle Saw 1 0.5 0..2 0
|
||||
//
|
||||
// The last morph uses uses half-height values larger than 1, which means it will
|
||||
// be louder and the output should be scaled down by half to compensate, or better
|
||||
// by dynamically tracking the morph: volume_scale = 1 - half_height/4
|
||||
//
|
||||
// Non-crossfade morph between classic waveforms, most require changing
|
||||
// two parameters at the same time:
|
||||
// peak half zero
|
||||
// Start End reflect time height wait
|
||||
// ----- --- ------- ---- ------ ----
|
||||
// Square Triangle any 0..0.5 1..0 0
|
||||
// Square Saw 1 0..1 1..any 0
|
||||
// Triangle Saw 1 0.5..1 0..-1 0
|
||||
//
|
||||
// Other noteworthy morphs between simple shapes:
|
||||
// peak half zero
|
||||
// Start Halfway End reflect time height wait
|
||||
// ----- --------- --- ------- ---- ------ ----
|
||||
// Saw (8va,neg) Saw (pos) 1 0..1 -1 0
|
||||
// Saw (neg) Saw (pos) 1 0..1 0 0
|
||||
// Triangle AlternatingSaw 0 0..1 -1 0
|
||||
// AlternatingSaw Triangle AlternatingSaw 0 0..1 0 0
|
||||
// Square AlternatingSaw 0 0..1 1 0
|
||||
// Triangle Triangle AlternatingSaw 0 0..1 -1..1 0
|
||||
// Square AlternatingSaw 0 0..1 1..0 0
|
||||
// Saw (8va) Triangle Saw 1 0..1 -1..1 0
|
||||
// Saw (neg) Saw (pos) 1 0..1 0..1 0
|
||||
// AlternatingSaw AlternatingSaw 0 0..1 0..any 0
|
||||
//
|
||||
// The last entry is noteworthy because the morph from the halfway point to either
|
||||
// endpoint sounds very different. For example, an LFO sweeping back and forth over
|
||||
// the whole range will morph between the middle timbre and the AlternatingSaw
|
||||
// timbre in two different ways, alternating.
|
||||
//
|
||||
// Entries with "any" for half_height are whole families of morphs, as you can pick
|
||||
// any value you want as the endpoint for half_height.
|
||||
//
|
||||
// You can always morph between any two waveforms with the same value of 'reflect'
|
||||
// by just sweeping the parameters simultaneously. There will never be artifacts
|
||||
// and the result will always be useful, if not necessarily what you want.
|
||||
//
|
||||
// You can vary the sound of two-parameter morphs by ramping them differently,
|
||||
// e.g. if the morph goes from t=0..1, then square-to-triangle looks like:
|
||||
// peak_time = lerp(t, 0, 0.5)
|
||||
// half_height = lerp(t, 1, 0 )
|
||||
// but you can also do things like:
|
||||
// peak_time = lerp(smoothstep(t), 0, 0.5)
|
||||
// half_height = cos(PI/2 * t)
|
||||
//
|
||||
// How it works:
|
||||
//
|
||||
// hexwave use BLEP to bandlimit discontinuities and BLAMP
|
||||
// to bandlimit C1 discontinuities. This is not polyBLEP
|
||||
// (polynomial BLEP), it is table-driven BLEP. It is
|
||||
// also not minBLEP (minimum-phase BLEP), as that complicates
|
||||
// things for little benefit once BLAMP is involved.
|
||||
//
|
||||
// The previous oscillator frequency is remembered, and when
|
||||
// the frequency changes, a BLAMP is generated to remove the
|
||||
// C1 discontinuity, which reduces artifacts for sweeps/LFO.
|
||||
//
|
||||
// Changes to an oscillator timbre using hexwave_change() actually
|
||||
// wait until the oscillator finishes its current cycle. All
|
||||
// waveforms with non-zero "zero_wait" settings pass through 0
|
||||
// and have 0-slope at the start of a cycle, which means changing
|
||||
// the settings is artifact free at that time. (If zero_wait is 0,
|
||||
// the code still treats it as passing through 0 with 0-slope; it'll
|
||||
// apply the necessary fixups to make it artifact free as if it does
|
||||
// transition to 0 with 0-slope vs. the waveform at the end of
|
||||
// the cycle, then adds the fixups for a non-0 and non-0 slope
|
||||
// at the start of the cycle, which cancels out if zero_wait is 0,
|
||||
// and still does the right thing if zero_wait is 0 when the
|
||||
// settings are updated.)
|
||||
//
|
||||
// BLEP/BLAMP normally requires overlapping buffers, but this
|
||||
// is hidden from the user by generating the waveform to a
|
||||
// temporary buffer and saving the overlap regions internally
|
||||
// between calls. (It is slightly more complicated; see code.)
|
||||
//
|
||||
// By design all shapes have 0 DC offset; this is one reason
|
||||
// hexwave uses zero_wait instead of standard PWM.
|
||||
//
|
||||
// The internals of hexwave could support any arbitrary shape
|
||||
// made of line segments, but I chose not to expose this
|
||||
// generality in favor of a simple, easy-to-use API.
|
||||
|
||||
#ifndef STB_INCLUDE_STB_HEXWAVE_H
|
||||
#define STB_INCLUDE_STB_HEXWAVE_H
|
||||
|
||||
#ifndef STB_HEXWAVE_MAX_BLEP_LENGTH
|
||||
#define STB_HEXWAVE_MAX_BLEP_LENGTH 64 // good enough for anybody
|
||||
#endif
|
||||
|
||||
#ifdef STB_HEXWAVE_STATIC
|
||||
#define STB_HEXWAVE_DEF static
|
||||
#else
|
||||
#define STB_HEXWAVE_DEF extern
|
||||
#endif
|
||||
|
||||
typedef struct HexWave HexWave;
|
||||
|
||||
STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer);
|
||||
// width: size of BLEP, from 4..64, larger is slower & more memory but less aliasing
|
||||
// oversample: 2+, number of subsample positions, larger uses more memory but less noise
|
||||
// user_buffer: optional, if provided the library will perform no allocations.
|
||||
// 16*width*(oversample+1) bytes, must stay allocated as long as library is used
|
||||
// technically it only needs: 8*( width * (oversample + 1))
|
||||
// + 8*((width * oversample) + 1) bytes
|
||||
//
|
||||
// width can be larger than 64 if you define STB_HEXWAVE_MAX_BLEP_LENGTH to a larger value
|
||||
|
||||
STB_HEXWAVE_DEF void hexwave_shutdown(float *user_buffer);
|
||||
// user_buffer: pass in same parameter as passed to hexwave_init
|
||||
|
||||
STB_HEXWAVE_DEF void hexwave_create(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait);
|
||||
// see docs above for description
|
||||
//
|
||||
// reflect is tested as 0 or non-zero
|
||||
// peak_time is clamped to 0..1
|
||||
// half_height is not clamped
|
||||
// zero_wait is clamped to 0..1
|
||||
|
||||
STB_HEXWAVE_DEF void hexwave_change(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait);
|
||||
// see docs
|
||||
|
||||
STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, HexWave *hex, float freq);
|
||||
// output: buffer where the library will store generated floating point audio samples
|
||||
// number_of_samples: the number of audio samples to generate
|
||||
// osc: pointer to a Hexwave initialized with 'hexwave_create'
|
||||
// oscillator_freq: frequency of the oscillator divided by the sample rate
|
||||
|
||||
// private:
|
||||
typedef struct
|
||||
{
|
||||
int reflect;
|
||||
float peak_time;
|
||||
float zero_wait;
|
||||
float half_height;
|
||||
} HexWaveParameters;
|
||||
|
||||
struct HexWave
|
||||
{
|
||||
float t, prev_dt;
|
||||
HexWaveParameters current, pending;
|
||||
int have_pending;
|
||||
float buffer[STB_HEXWAVE_MAX_BLEP_LENGTH];
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef STB_HEXWAVE_IMPLEMENTATION
|
||||
|
||||
#ifndef STB_HEXWAVE_NO_ALLOCATION
|
||||
#include <stdlib.h> // malloc,free
|
||||
#endif
|
||||
|
||||
#include <string.h> // memset,memcpy,memmove
|
||||
#include <math.h> // sin,cos,fabs
|
||||
|
||||
#define hexwave_clamp(v,a,b) ((v) < (a) ? (a) : (v) > (b) ? (b) : (v))
|
||||
|
||||
STB_HEXWAVE_DEF void hexwave_change(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait)
|
||||
{
|
||||
hex->pending.reflect = reflect;
|
||||
hex->pending.peak_time = hexwave_clamp(peak_time,0,1);
|
||||
hex->pending.half_height = half_height;
|
||||
hex->pending.zero_wait = hexwave_clamp(zero_wait,0,1);
|
||||
// put a barrier here to allow changing from a different thread than the generator
|
||||
hex->have_pending = 1;
|
||||
}
|
||||
|
||||
STB_HEXWAVE_DEF void hexwave_create(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait)
|
||||
{
|
||||
memset(hex, 0, sizeof(*hex));
|
||||
hexwave_change(hex, reflect, peak_time, half_height, zero_wait);
|
||||
hex->current = hex->pending;
|
||||
hex->have_pending = 0;
|
||||
hex->t = 0;
|
||||
hex->prev_dt = 0;
|
||||
}
|
||||
|
||||
static struct
|
||||
{
|
||||
int width; // width of fixup in samples
|
||||
int oversample; // number of oversampled versions (there's actually one more to allow lerpign)
|
||||
float *blep;
|
||||
float *blamp;
|
||||
} hexblep;
|
||||
|
||||
static void hex_add_oversampled_bleplike(float *output, float time_since_transition, float scale, float *data)
|
||||
{
|
||||
float *d1,*d2;
|
||||
float lerpweight;
|
||||
int i, bw = hexblep.width;
|
||||
|
||||
int slot = (int) (time_since_transition * hexblep.oversample);
|
||||
if (slot >= hexblep.oversample)
|
||||
slot = hexblep.oversample-1; // clamp in case the floats overshoot
|
||||
|
||||
d1 = &data[ slot *bw];
|
||||
d2 = &data[(slot+1)*bw];
|
||||
|
||||
lerpweight = time_since_transition * hexblep.oversample - slot;
|
||||
for (i=0; i < bw; ++i)
|
||||
output[i] += scale * (d1[i] + (d2[i]-d1[i])*lerpweight);
|
||||
}
|
||||
|
||||
static void hex_blep (float *output, float time_since_transition, float scale)
|
||||
{
|
||||
hex_add_oversampled_bleplike(output, time_since_transition, scale, hexblep.blep);
|
||||
}
|
||||
|
||||
static void hex_blamp(float *output, float time_since_transition, float scale)
|
||||
{
|
||||
hex_add_oversampled_bleplike(output, time_since_transition, scale, hexblep.blamp);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float t,v,s; // time, value, slope
|
||||
} hexvert;
|
||||
|
||||
// each half of the waveform needs 4 vertices to represent 3 line
|
||||
// segments, plus 1 more for wraparound
|
||||
static void hexwave_generate_linesegs(hexvert vert[9], HexWave *hex, float dt)
|
||||
{
|
||||
int j;
|
||||
float min_len = dt / 256.0f;
|
||||
|
||||
vert[0].t = 0;
|
||||
vert[0].v = 0;
|
||||
vert[1].t = hex->current.zero_wait*0.5f;
|
||||
vert[1].v = 0;
|
||||
vert[2].t = 0.5f*hex->current.peak_time + vert[1].t*(1-hex->current.peak_time);
|
||||
vert[2].v = 1;
|
||||
vert[3].t = 0.5f;
|
||||
vert[3].v = hex->current.half_height;
|
||||
|
||||
if (hex->current.reflect) {
|
||||
for (j=4; j <= 7; ++j) {
|
||||
vert[j].t = 1 - vert[7-j].t;
|
||||
vert[j].v = - vert[7-j].v;
|
||||
}
|
||||
} else {
|
||||
for (j=4; j <= 7; ++j) {
|
||||
vert[j].t = 0.5f + vert[j-4].t;
|
||||
vert[j].v = - vert[j-4].v;
|
||||
}
|
||||
}
|
||||
vert[8].t = 1;
|
||||
vert[8].v = 0;
|
||||
|
||||
for (j=0; j < 8; ++j) {
|
||||
if (vert[j+1].t <= vert[j].t + min_len) {
|
||||
// if change takes place over less than a fraction of a sample treat as discontinuity
|
||||
//
|
||||
// otherwise the slope computation can blow up to arbitrarily large and we
|
||||
// try to generate a huge BLAMP and the result is wrong.
|
||||
//
|
||||
// why does this happen if the math is right? i believe if done perfectly,
|
||||
// the two BLAMPs on either side of the slope would cancel out, but our
|
||||
// BLAMPs have only limited sub-sample precision and limited integration
|
||||
// accuracy. or maybe it's just the math blowing up w/ floating point precision
|
||||
// limits as we try to make x * (1/x) cancel out
|
||||
//
|
||||
// min_len verified artifact-free even near nyquist with only oversample=4
|
||||
vert[j+1].t = vert[j].t;
|
||||
}
|
||||
}
|
||||
|
||||
if (vert[8].t != 1.0f) {
|
||||
// if the above fixup moved the endpoint away from 1.0, move it back,
|
||||
// along with any other vertices that got moved to the same time
|
||||
float t = vert[8].t;
|
||||
for (j=5; j <= 8; ++j)
|
||||
if (vert[j].t == t)
|
||||
vert[j].t = 1.0f;
|
||||
}
|
||||
|
||||
// compute the exact slopes from the final fixed-up positions
|
||||
for (j=0; j < 8; ++j)
|
||||
if (vert[j+1].t == vert[j].t)
|
||||
vert[j].s = 0;
|
||||
else
|
||||
vert[j].s = (vert[j+1].v - vert[j].v) / (vert[j+1].t - vert[j].t);
|
||||
|
||||
// wraparound at end
|
||||
vert[8].t = 1;
|
||||
vert[8].v = vert[0].v;
|
||||
vert[8].s = vert[0].s;
|
||||
}
|
||||
|
||||
STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, HexWave *hex, float freq)
|
||||
{
|
||||
hexvert vert[9];
|
||||
int pass,i,j;
|
||||
float t = hex->t;
|
||||
float temp_output[2*STB_HEXWAVE_MAX_BLEP_LENGTH];
|
||||
int buffered_length = sizeof(float)*hexblep.width;
|
||||
float dt = (float) fabs(freq);
|
||||
float recip_dt = (dt == 0.0f) ? 0.0f : 1.0f / dt;
|
||||
|
||||
int halfw = hexblep.width/2;
|
||||
// all sample times are biased by halfw to leave room for BLEP/BLAMP to go back in time
|
||||
|
||||
if (num_samples <= 0)
|
||||
return;
|
||||
|
||||
// convert parameters to times and slopes
|
||||
hexwave_generate_linesegs(vert, hex, dt);
|
||||
|
||||
if (hex->prev_dt != dt) {
|
||||
// if frequency changes, add a fixup at the derivative discontinuity starting at now
|
||||
float slope;
|
||||
for (j=1; j < 6; ++j)
|
||||
if (t < vert[j].t)
|
||||
break;
|
||||
slope = vert[j].s;
|
||||
if (slope != 0)
|
||||
hex_blamp(output, 0, (dt - hex->prev_dt)*slope);
|
||||
hex->prev_dt = dt;
|
||||
}
|
||||
|
||||
// copy the buffered data from last call and clear the rest of the output array
|
||||
memset(output, 0, sizeof(float)*num_samples);
|
||||
memset(temp_output, 0, 2*hexblep.width*sizeof(float));
|
||||
|
||||
if (num_samples >= hexblep.width) {
|
||||
memcpy(output, hex->buffer, buffered_length);
|
||||
} else {
|
||||
// if the output is shorter than hexblep.width, we do all synthesis to temp_output
|
||||
memcpy(temp_output, hex->buffer, buffered_length);
|
||||
}
|
||||
|
||||
for (pass=0; pass < 2; ++pass) {
|
||||
int i0,i1;
|
||||
float *out;
|
||||
|
||||
// we want to simulate having one buffer that is num_output + hexblep.width
|
||||
// samples long, without putting that requirement on the user, and without
|
||||
// allocating a temp buffer that's as long as the whole thing. so we use two
|
||||
// overlapping buffers, one the user's buffer and one a fixed-length temp
|
||||
// buffer.
|
||||
|
||||
if (pass == 0) {
|
||||
if (num_samples < hexblep.width)
|
||||
continue;
|
||||
// run as far as we can without overwriting the end of the user's buffer
|
||||
out = output;
|
||||
i0 = 0;
|
||||
i1 = num_samples - hexblep.width;
|
||||
} else {
|
||||
// generate the rest into a temp buffer
|
||||
out = temp_output;
|
||||
i0 = 0;
|
||||
if (num_samples >= hexblep.width)
|
||||
i1 = hexblep.width;
|
||||
else
|
||||
i1 = num_samples;
|
||||
}
|
||||
|
||||
// determine current segment
|
||||
for (j=0; j < 8; ++j)
|
||||
if (t < vert[j+1].t)
|
||||
break;
|
||||
|
||||
i = i0;
|
||||
for(;;) {
|
||||
while (t < vert[j+1].t) {
|
||||
if (i == i1)
|
||||
goto done;
|
||||
out[i+halfw] += vert[j].v + vert[j].s*(t - vert[j].t);
|
||||
t += dt;
|
||||
++i;
|
||||
}
|
||||
// transition from lineseg starting at j to lineseg starting at j+1
|
||||
|
||||
if (vert[j].t == vert[j+1].t)
|
||||
hex_blep(out+i, recip_dt*(t-vert[j+1].t), (vert[j+1].v - vert[j].v));
|
||||
hex_blamp(out+i, recip_dt*(t-vert[j+1].t), dt*(vert[j+1].s - vert[j].s));
|
||||
++j;
|
||||
|
||||
if (j == 8) {
|
||||
// change to different waveform if there's a change pending
|
||||
j = 0;
|
||||
t -= 1.0; // t was >= 1.f if j==8
|
||||
if (hex->have_pending) {
|
||||
float prev_s0 = vert[j].s;
|
||||
float prev_v0 = vert[j].v;
|
||||
hex->current = hex->pending;
|
||||
hex->have_pending = 0;
|
||||
hexwave_generate_linesegs(vert, hex, dt);
|
||||
// the following never occurs with this oscillator, but it makes
|
||||
// the code work in more general cases
|
||||
if (vert[j].v != prev_v0)
|
||||
hex_blep (out+i, recip_dt*t, (vert[j].v - prev_v0));
|
||||
if (vert[j].s != prev_s0)
|
||||
hex_blamp(out+i, recip_dt*t, dt*(vert[j].s - prev_s0));
|
||||
}
|
||||
}
|
||||
}
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
// at this point, we've written output[] and temp_output[]
|
||||
if (num_samples >= hexblep.width) {
|
||||
// the first half of temp[] overlaps the end of output, the second half will be the new start overlap
|
||||
for (i=0; i < hexblep.width; ++i)
|
||||
output[num_samples-hexblep.width + i] += temp_output[i];
|
||||
memcpy(hex->buffer, temp_output+hexblep.width, buffered_length);
|
||||
} else {
|
||||
for (i=0; i < num_samples; ++i)
|
||||
output[i] = temp_output[i];
|
||||
memcpy(hex->buffer, temp_output+num_samples, buffered_length);
|
||||
}
|
||||
|
||||
hex->t = t;
|
||||
}
|
||||
|
||||
STB_HEXWAVE_DEF void hexwave_shutdown(float *user_buffer)
|
||||
{
|
||||
#ifndef STB_HEXWAVE_NO_ALLOCATION
|
||||
if (user_buffer != 0) {
|
||||
free(hexblep.blep);
|
||||
free(hexblep.blamp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// buffer should be NULL or must be 4*(width*(oversample+1)*2 +
|
||||
STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer)
|
||||
{
|
||||
int halfwidth = width/2;
|
||||
int half = halfwidth*oversample;
|
||||
int blep_buffer_count = width*(oversample+1);
|
||||
int n = 2*half+1;
|
||||
#ifdef STB_HEXWAVE_NO_ALLOCATION
|
||||
float *buffers = user_buffer;
|
||||
#else
|
||||
float *buffers = user_buffer ? user_buffer : (float *) malloc(sizeof(float) * n * 2);
|
||||
#endif
|
||||
float *step = buffers+0*n;
|
||||
float *ramp = buffers+1*n;
|
||||
float *blep_buffer, *blamp_buffer;
|
||||
double integrate_impulse=0, integrate_step=0;
|
||||
int i,j;
|
||||
|
||||
if (width > STB_HEXWAVE_MAX_BLEP_LENGTH)
|
||||
width = STB_HEXWAVE_MAX_BLEP_LENGTH;
|
||||
|
||||
if (user_buffer == 0) {
|
||||
#ifndef STB_HEXWAVE_NO_ALLOCATION
|
||||
blep_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
|
||||
blamp_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
|
||||
#endif
|
||||
} else {
|
||||
blep_buffer = ramp+n;
|
||||
blamp_buffer = blep_buffer + blep_buffer_count;
|
||||
}
|
||||
|
||||
// compute BLEP and BLAMP by integerating windowed sinc
|
||||
for (i=0; i < n; ++i) {
|
||||
for (j=0; j < 16; ++j) {
|
||||
float sinc_t = 3.141592f* (i-half) / oversample;
|
||||
float sinc = (i==half) ? 1.0f : (float) sin(sinc_t) / (sinc_t);
|
||||
float wt = 2.0f*3.1415926f * i / (n-1);
|
||||
float window = (float) (0.355768 - 0.487396*cos(wt) + 0.144232*cos(2*wt) - 0.012604*cos(3*wt)); // Nuttall
|
||||
double value = window * sinc;
|
||||
integrate_impulse += value/16;
|
||||
integrate_step += integrate_impulse/16;
|
||||
}
|
||||
step[i] = (float) integrate_impulse;
|
||||
ramp[i] = (float) integrate_step;
|
||||
}
|
||||
|
||||
// renormalize
|
||||
for (i=0; i < n; ++i) {
|
||||
step[i] = step[i] * (float) (1.0 / step[n-1]); // step needs to reach to 1.0
|
||||
ramp[i] = ramp[i] * (float) (halfwidth / ramp[n-1]); // ramp needs to become a slope of 1.0 after oversampling
|
||||
}
|
||||
|
||||
// deinterleave to allow efficient interpolation e.g. w/SIMD
|
||||
for (j=0; j <= oversample; ++j) {
|
||||
for (i=0; i < width; ++i) {
|
||||
blep_buffer [j*width+i] = step[j+i*oversample];
|
||||
blamp_buffer[j*width+i] = ramp[j+i*oversample];
|
||||
}
|
||||
}
|
||||
|
||||
// subtract out the naive waveform; note we can't do this to the raw data
|
||||
// above, because we want the discontinuity to be in a different locations
|
||||
// for j=0 and j=oversample (which exists to provide something to interpolate against)
|
||||
for (j=0; j <= oversample; ++j) {
|
||||
// subtract step
|
||||
for (i=halfwidth; i < width; ++i)
|
||||
blep_buffer [j*width+i] -= 1.0f;
|
||||
// subtract ramp
|
||||
for (i=halfwidth; i < width; ++i)
|
||||
blamp_buffer[j*width+i] -= (j+i*oversample-half)*(1.0f/oversample);
|
||||
}
|
||||
|
||||
hexblep.blep = blep_buffer;
|
||||
hexblep.blamp = blamp_buffer;
|
||||
hexblep.width = width;
|
||||
hexblep.oversample = oversample;
|
||||
|
||||
#ifndef STB_HEXWAVE_NO_ALLOCATION
|
||||
if (user_buffer == 0)
|
||||
free(buffers);
|
||||
#endif
|
||||
}
|
||||
#endif // STB_HEXWAVE_IMPLEMENTATION
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||