good comp panel
This commit is contained in:
parent
6eaa92cdf8
commit
2f7378f218
16 changed files with 2387 additions and 344 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -7,3 +7,6 @@
|
||||||
[submodule "Keishiki/ext/freetype"]
|
[submodule "Keishiki/ext/freetype"]
|
||||||
path = Keishiki/ext/freetype
|
path = Keishiki/ext/freetype
|
||||||
url = https://github.com/freetype/freetype.git
|
url = https://github.com/freetype/freetype.git
|
||||||
|
[submodule "Keishiki/ext/ImNodeFlow"]
|
||||||
|
path = Keishiki/ext/ImNodeFlow
|
||||||
|
url = https://github.com/Fattorino/ImNodeFlow.git
|
||||||
|
|
|
@ -11,11 +11,20 @@ add_executable (Keishiki
|
||||||
"ext/imgui/misc/cpp/imgui_stdlib.cpp"
|
"ext/imgui/misc/cpp/imgui_stdlib.cpp"
|
||||||
|
|
||||||
"Keishiki.cpp" "Graphics.cpp" "UI.cpp" "ShaderGraph.cpp")
|
"Keishiki.cpp" "Graphics.cpp" "UI.cpp" "ShaderGraph.cpp")
|
||||||
|
|
||||||
|
|
||||||
set_property(TARGET Keishiki PROPERTY CXX_STANDARD 23)
|
set_property(TARGET Keishiki PROPERTY CXX_STANDARD 23)
|
||||||
|
|
||||||
include_directories ("include" "include/ext" "ext/imgui")
|
include_directories ("include" "include/ext" "ext/imgui")
|
||||||
|
|
||||||
add_subdirectory("ext/freetype")
|
add_subdirectory("ext/freetype")
|
||||||
add_subdirectory("ext/bgfx")
|
add_subdirectory("ext/bgfx")
|
||||||
|
|
||||||
|
#add_subdirectory("ext/ImNodeFlow")
|
||||||
|
add_compile_definitions(IMGUI_DEFINE_MATH_OPERATORS)
|
||||||
|
#target_link_libraries(Keishiki ImNodeFlow)
|
||||||
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
include_directories("include/windows")
|
include_directories("include/windows")
|
||||||
target_link_libraries (Keishiki PRIVATE freetype bgfx bx ${PROJECT_SOURCE_DIR}/lib/x64/SDL2.lib ${PROJECT_SOURCE_DIR}/lib/x64/SDL2main.lib)
|
target_link_libraries (Keishiki PRIVATE freetype bgfx bx ${PROJECT_SOURCE_DIR}/lib/x64/SDL2.lib ${PROJECT_SOURCE_DIR}/lib/x64/SDL2main.lib)
|
||||||
|
|
|
@ -18,9 +18,11 @@ namespace {
|
||||||
u16 window_height = 1080;
|
u16 window_height = 1080;
|
||||||
|
|
||||||
u64 init_time, last_time, current_time, delta_t;
|
u64 init_time, last_time, current_time, delta_t;
|
||||||
|
u32 frame;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
|
|
||||||
K::CompState state;
|
K::CompState state;
|
||||||
|
K::Graphics::ImageTexture *logo;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace K {
|
namespace K {
|
||||||
|
@ -72,10 +74,14 @@ namespace K {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bgfx::setDebug(BGFX_DEBUG_TEXT);
|
bgfx::setDebug(BGFX_DEBUG_TEXT);
|
||||||
bgfx::setViewClear(K::Graphics::K_VIEW_TOP, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x000000ff, 1.0f, 0);
|
bgfx::setViewClear(K::Graphics::K_VIEW_BG, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x000000ff, 1.0f, 0);
|
||||||
bgfx::setViewRect(K::Graphics::K_VIEW_TOP, 0, 0, window_width, window_height);
|
bgfx::setViewRect(K::Graphics::K_VIEW_BG, 0, 0, window_width, window_height);
|
||||||
|
float view[16];
|
||||||
|
bx::mtxTranslate(view, 0.f, 0.f, 1.0f);
|
||||||
|
float proj[16];
|
||||||
|
bx::mtxOrtho(proj, 0.0f, window_width, 0.0f, window_height, 0.1f, 100.0f, 0.f, bgfx::getCaps()->homogeneousDepth);
|
||||||
|
bgfx::setViewTransform(K::Graphics::K_VIEW_BG, view, proj);
|
||||||
|
|
||||||
K::UI::Init(window);
|
K::UI::Init(window);
|
||||||
|
|
||||||
|
@ -84,25 +90,35 @@ namespace K {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.width = 800;
|
logo = K::Graphics::GetImageTextureFromFile("Keishiki.png");
|
||||||
state.height = 600;
|
|
||||||
|
state.width = 1280;
|
||||||
|
state.height = 550;
|
||||||
K::UI::SetupViewport(state);
|
K::UI::SetupViewport(state);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Render() {
|
void Render() {
|
||||||
UI::Draw(state);
|
K::Graphics::DrawTextureImageAlpha(K::Graphics::K_VIEW_BG, *logo, window_width - logo->w + 10, 0, .7f);
|
||||||
|
|
||||||
|
UI::Draw(frame, state);
|
||||||
|
|
||||||
const bgfx::Stats *stat = bgfx::getStats();
|
const bgfx::Stats *stat = bgfx::getStats();
|
||||||
const bgfx::Caps *caps = bgfx::getCaps();
|
const bgfx::Caps *caps = bgfx::getCaps();
|
||||||
bgfx::dbgTextClear();
|
bgfx::dbgTextClear();
|
||||||
bgfx::dbgTextPrintf(0, window_height/16 - 4, 0xf8, " lachrymal.net :: %s Renderer :: %u FPS", caps->supported & BGFX_CAPS_RENDERER_MULTITHREADED ? "Multithreaded" : "Singlethreaded", stat->cpuTimerFreq / stat->cpuTimeFrame);
|
bgfx::dbgTextPrintf(0, window_height/16 - 2, 0xf8,
|
||||||
bgfx::dbgTextPrintf(0, window_height/16 - 3, 0xf8, " Max Views %u :: Max FBs %u :: Max Texture Samplers %u :: Blitting %s", caps->limits.maxViews, caps->limits.maxFrameBuffers, caps->limits.maxTextureSamplers, caps->supported & BGFX_CAPS_TEXTURE_BLIT ? "OK" : "NO");
|
" lachrymal.net :: %s Renderer :: Max Views %u :: Max FBs %u :: Max Texture Samplers %u :: Blitting %s :: %u FPS",
|
||||||
bgfx::dbgTextPrintf(0, window_height/16 - 2, 0xf8, " Project Keishiki :: %s :: Build %s %s", BX_COMPILER_NAME, __DATE__, __TIME__);
|
caps->supported & BGFX_CAPS_RENDERER_MULTITHREADED ? "Multithreaded" : "Singlethreaded",
|
||||||
bgfx::dbgTextPrintf(0, window_height/16 - 1, 0xf8, " SDL %i.%i.%i :: bgfx 1.%i :: Dear ImGui %s :: %s", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL, BGFX_API_VERSION, ImGui::GetVersion(), bgfx::getRendererName(bgfx::getRendererType()));
|
caps->limits.maxViews, caps->limits.maxFrameBuffers,
|
||||||
|
caps->limits.maxTextureSamplers, caps->supported & BGFX_CAPS_TEXTURE_BLIT ? "OK" : "NO",
|
||||||
|
stat->cpuTimerFreq / stat->cpuTimeFrame);
|
||||||
|
bgfx::dbgTextPrintf(0, window_height/16 - 1, 0xf8,
|
||||||
|
" Project Keishiki :: %s :: Build %s %s :: SDL %i.%i.%i :: bgfx 1.%i :: Dear ImGui %s :: %s",
|
||||||
|
BX_COMPILER_NAME, __DATE__, __TIME__, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
|
||||||
|
BGFX_API_VERSION, ImGui::GetVersion(), bgfx::getRendererName(bgfx::getRendererType()));
|
||||||
|
|
||||||
bgfx::frame();
|
frame = bgfx::frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Run() {
|
void Run() {
|
||||||
|
|
426
Keishiki/UI.cpp
426
Keishiki/UI.cpp
|
@ -1,5 +1,4 @@
|
||||||
#include "UI.h"
|
#include "UI.h"
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
||||||
|
|
||||||
#include "ext/imgui/misc/cpp/imgui_stdlib.h"
|
#include "ext/imgui/misc/cpp/imgui_stdlib.h"
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
@ -9,19 +8,30 @@
|
||||||
#include "backends/imgui_impl_sdl2.h"
|
#include "backends/imgui_impl_sdl2.h"
|
||||||
#include "imgui_impl_bgfx.h"
|
#include "imgui_impl_bgfx.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool draw_viewport = true;
|
// windows
|
||||||
bool draw_sequencer = true;
|
bool draw_viewport = true,
|
||||||
bool draw_nodes = true;
|
draw_plugboard = true,
|
||||||
|
draw_shader = true,
|
||||||
|
draw_comp = true;
|
||||||
|
|
||||||
|
const f32 row_height = 20.0f;
|
||||||
|
|
||||||
// Viewport
|
// Viewport
|
||||||
bgfx::TextureHandle composite = BGFX_INVALID_HANDLE,
|
bgfx::TextureHandle composite = BGFX_INVALID_HANDLE,
|
||||||
composite_blit = BGFX_INVALID_HANDLE,
|
composite_blit = BGFX_INVALID_HANDLE,
|
||||||
render = BGFX_INVALID_HANDLE;
|
render = BGFX_INVALID_HANDLE,
|
||||||
|
save = BGFX_INVALID_HANDLE;
|
||||||
bgfx::FrameBufferHandle composite_fb = BGFX_INVALID_HANDLE,
|
bgfx::FrameBufferHandle composite_fb = BGFX_INVALID_HANDLE,
|
||||||
render_fb = BGFX_INVALID_HANDLE;
|
render_fb = BGFX_INVALID_HANDLE;
|
||||||
K::VisualTrack bg{};
|
K::VisualTrack bg{};
|
||||||
f32 proj[16], transform[16], view[16];
|
f32 proj[16], transform[16], view[16];
|
||||||
|
K::Byte *save_buffer;
|
||||||
|
bool save_called = false;
|
||||||
|
u32 ready_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,20 +49,20 @@ namespace ImGui {
|
||||||
namespace K::UI {
|
namespace K::UI {
|
||||||
void MainMenuBar(const CompState& s) {
|
void MainMenuBar(const CompState& s) {
|
||||||
if (ImGui::BeginMainMenuBar()) {
|
if (ImGui::BeginMainMenuBar()) {
|
||||||
//if (ImGui::BeginMenu("Edit")) {
|
if (ImGui::BeginMenu("Edit")) {
|
||||||
// if (ImGui::MenuItem("Undo", "CTRL+Z")) {}
|
// if (ImGui::MenuItem("Undo", "CTRL+Z")) {}
|
||||||
// if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item
|
// if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item
|
||||||
// ImGui::Separator();
|
// ImGui::Separator();
|
||||||
// if (ImGui::MenuItem("Cut", "CTRL+X")) {}
|
// if (ImGui::MenuItem("Cut", "CTRL+X")) {}
|
||||||
// if (ImGui::MenuItem("Copy", "CTRL+C")) {}
|
// if (ImGui::MenuItem("Copy", "CTRL+C")) {}
|
||||||
// if (ImGui::MenuItem("Paste", "CTRL+V")) {}
|
// if (ImGui::MenuItem("Paste", "CTRL+V")) {}
|
||||||
// ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
//}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginMenu("View")) {
|
if (ImGui::BeginMenu("View")) {
|
||||||
if (ImGui::MenuItem("Viewport", "", &draw_viewport)) {}
|
if (ImGui::MenuItem("Viewport", "", &draw_viewport)) {}
|
||||||
if (ImGui::MenuItem("Sequencer", "", &draw_sequencer)) {}
|
if (ImGui::MenuItem("Plugboard", "", &draw_plugboard)) {}
|
||||||
if (ImGui::MenuItem("Shader", "", &draw_nodes)) {}
|
if (ImGui::MenuItem("Shader", "", &draw_shader)) {}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +79,9 @@ namespace K::UI {
|
||||||
composite_fb = bgfx::createFrameBuffer(1, &comp);
|
composite_fb = bgfx::createFrameBuffer(1, &comp);
|
||||||
render_fb = bgfx::createFrameBuffer(1, &ren);
|
render_fb = bgfx::createFrameBuffer(1, &ren);
|
||||||
|
|
||||||
|
save = bgfx::createTexture2D(s.width, s.height, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_READ_BACK | BGFX_TEXTURE_BLIT_DST);
|
||||||
|
save_buffer = static_cast<Byte *>(std::malloc(s.width * s.height * 4));
|
||||||
|
|
||||||
bg.pg = K::Graphics::load_shader_program("Checkerboard");
|
bg.pg = K::Graphics::load_shader_program("Checkerboard");
|
||||||
bg.add_uniform("f_hw", ShaderGraph::Type::T_XYZ);
|
bg.add_uniform("f_hw", ShaderGraph::Type::T_XYZ);
|
||||||
bg.uniforms.begin()->second.second = ShaderGraph::XYZ{ static_cast<f32>(s.width), static_cast<f32>(s.height), 0.0f };
|
bg.uniforms.begin()->second.second = ShaderGraph::XYZ{ static_cast<f32>(s.width), static_cast<f32>(s.height), 0.0f };
|
||||||
|
@ -89,18 +102,15 @@ namespace K::UI {
|
||||||
bgfx::destroy(render_fb);
|
bgfx::destroy(render_fb);
|
||||||
bgfx::destroy(render);
|
bgfx::destroy(render);
|
||||||
|
|
||||||
|
bgfx::destroy(save);
|
||||||
|
std::free(save_buffer);
|
||||||
|
|
||||||
bgfx::destroy(bg.pg);
|
bgfx::destroy(bg.pg);
|
||||||
bgfx::destroy(bg.uniforms.begin()->second.first);
|
bgfx::destroy(bg.uniforms.begin()->second.first);
|
||||||
bg.uniforms.erase(bg.uniforms.begin());
|
bg.uniforms.erase(bg.uniforms.begin());
|
||||||
|
|
||||||
for (auto& layer : s.layers) {
|
for (auto& layer : s.layers)
|
||||||
if (bgfx::isValid(layer.track.pg))
|
layer.track.clear();
|
||||||
bgfx::destroy(layer.track.pg);
|
|
||||||
for (auto& uniform : layer.track.uniforms) {
|
|
||||||
bgfx::destroy(uniform.second.first);
|
|
||||||
}
|
|
||||||
layer.track.uniforms.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewport(CompState& s) {
|
void Viewport(CompState& s) {
|
||||||
|
@ -109,103 +119,248 @@ namespace K::UI {
|
||||||
|
|
||||||
bg.get_frame(composite_fb, s.width, s.height, proj, view, transform);
|
bg.get_frame(composite_fb, s.width, s.height, proj, view, transform);
|
||||||
bgfx::blit(Graphics::K_VIEW_COMP_COMPOSITE, composite_blit, 0, 0, composite);
|
bgfx::blit(Graphics::K_VIEW_COMP_COMPOSITE, composite_blit, 0, 0, composite);
|
||||||
for (auto it = s.layers.rbegin(); it != s.layers.rend(); it++) {
|
for (u32 i = s.layers.size() - 1; i != u32(-1); i--) {
|
||||||
if (!it->enabled) continue;
|
if (s.disabled.contains(i)) continue;
|
||||||
Graphics::Composite(composite_fb, composite_blit, it->track.get_frame(render_fb, s.width, s.height, proj, view, transform),
|
Graphics::Composite(composite_fb, composite_blit, s.layers[i].track.get_frame(render_fb, s.width, s.height, proj, view, transform),
|
||||||
it == s.layers.rbegin() ? Graphics::K_V_AlphaOver : it->mode, s.width, s.height, proj, transform);
|
i == s.layers.size() - 1 ? Graphics::K_V_AlphaOver : s.layers[i].mode, s.width, s.height, proj, transform);
|
||||||
bgfx::blit(Graphics::K_VIEW_COMP_COMPOSITE, composite_blit, 0, 0, composite);
|
bgfx::blit(Graphics::K_VIEW_COMP_COMPOSITE, composite_blit, 0, 0, composite);
|
||||||
}
|
}
|
||||||
idx = ImGui::toId(composite, 0, 0);
|
idx = ImGui::toId(composite, 0, 0);
|
||||||
if (idx != nullptr)
|
if (idx != nullptr)
|
||||||
ImGui::Image(idx, ImVec2{ static_cast<f32>(s.width), static_cast<f32>(s.height) });
|
ImGui::Image(idx, ImVec2{ static_cast<f32>(s.width), static_cast<f32>(s.height) });
|
||||||
|
|
||||||
ImGui::Text("Comp Size: %ux%u", s.width, s.height);
|
ImGui::Text("Composition Size: %ux%u", s.width, s.height);
|
||||||
|
|
||||||
|
|
||||||
|
auto cp = save_called;
|
||||||
|
if (cp) ImGui::BeginDisabled();
|
||||||
|
if (ImGui::Button("Save to frame.png")) {
|
||||||
|
bgfx::blit(Graphics::K_VIEW_COMP_SAVE, save, 0, 0, composite);
|
||||||
|
ready_frame = bgfx::readTexture(save, save_buffer);
|
||||||
|
save_called = true;
|
||||||
|
}
|
||||||
|
if (cp) ImGui::EndDisabled();
|
||||||
|
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sequencer(CompState& s){
|
void Composition(CompState& s) {
|
||||||
if (ImGui::Begin("Sequencer", &draw_sequencer)) {
|
if (!ImGui::Begin("Composition", &draw_plugboard)) {
|
||||||
static String name{};
|
|
||||||
if (ImGui::Button("Add Layer") && !name.empty()) {
|
|
||||||
s.layers.push_back(Layer{
|
|
||||||
VisualTrack{},
|
|
||||||
name,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
Graphics::K_V_AlphaOver
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::InputText("##UniformName", &name);
|
|
||||||
|
|
||||||
|
|
||||||
if (ImGui::BeginTable("Layers", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
|
|
||||||
ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort);
|
|
||||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort);
|
|
||||||
ImGui::TableSetupColumn("Blending", ImGuiTableColumnFlags_NoSort);
|
|
||||||
ImGui::TableSetupColumn("Enable", ImGuiTableColumnFlags_NoSort);
|
|
||||||
ImGui::TableSetupColumn("##", ImGuiTableColumnFlags_NoSort);
|
|
||||||
ImGui::TableHeadersRow();
|
|
||||||
for (u32 i = 0; i < s.layers.size(); i++) {
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("%u", i);
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
if (ImGui::Selectable((s.layers[i].name + "##" + std::to_string(i)).c_str(), &s.layers[i].selected)) {
|
|
||||||
s.active = s.layers[i].selected ? i : -1;
|
|
||||||
if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held
|
|
||||||
for (u32 j = 0; j < s.layers.size(); j++)
|
|
||||||
s.layers[j].selected = j == i && s.layers[j].selected;
|
|
||||||
}
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::SetNextItemWidth(100.0f);
|
|
||||||
if (ImGui::BeginCombo(("##Blending" + std::to_string(i)).c_str(), Graphics::BlendingToString[s.layers[i].mode])) {
|
|
||||||
for (i32 b = Graphics::K_V_AlphaOver; b != Graphics::K_V_Count; b++) {
|
|
||||||
const bool is_selected = (static_cast<Graphics::Blending>(b) == s.layers[i].mode);
|
|
||||||
if (ImGui::Selectable(Graphics::BlendingToString[b], is_selected))
|
|
||||||
s.layers[i].mode = static_cast<Graphics::Blending>(b);
|
|
||||||
if (is_selected)
|
|
||||||
ImGui::SetItemDefaultFocus();
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Checkbox(("##Enable" + std::to_string(i)).c_str(), &s.layers[i].enabled);
|
|
||||||
}
|
|
||||||
ImGui::EndTable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*void Nodegraph(CompState& s) {
|
|
||||||
Layer* active = nullptr;
|
|
||||||
u32 active_index = 0;
|
|
||||||
while (active_index < s.layers.size()) {
|
|
||||||
if (s.layers[active_index].selected) {
|
|
||||||
active = &s.layers[active_index];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
active_index++;
|
|
||||||
} // should not need to do this every frame
|
|
||||||
|
|
||||||
if (!ImGui::Begin("Nodegraph", &draw_nodes)) {
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool no_selection = s.selected.empty();
|
||||||
|
|
||||||
|
static String name{};
|
||||||
|
if (ImGui::Button("Add Layer") && !name.empty()) {
|
||||||
|
auto l = Layer{
|
||||||
|
VisualTrack{},
|
||||||
|
name,
|
||||||
|
Graphics::K_V_AlphaOver
|
||||||
|
};
|
||||||
|
l.track.add_uniform("aspect_ratio", ShaderGraph::expand_type(ShaderGraph::T_XY));
|
||||||
|
if (s.width > s.height)
|
||||||
|
l.track.uniforms["aspect_ratio"].second = ShaderGraph::T_Map<ShaderGraph::T_XY>::type(static_cast<f32>(s.width)/static_cast<f32>(s.height), 1.0f);
|
||||||
|
else
|
||||||
|
l.track.uniforms["aspect_ratio"].second = ShaderGraph::T_Map<ShaderGraph::T_XY>::type(1.0f, static_cast<f32>(s.height)/static_cast<f32>(s.width));
|
||||||
|
|
||||||
|
l.track.add_uniform("opacity", ShaderGraph::expand_type(ShaderGraph::T_Float));
|
||||||
|
l.track.uniforms["opacity"].second = ShaderGraph::T_Map<ShaderGraph::T_Float>::type(1.0f);
|
||||||
|
|
||||||
|
l.track.add_uniform("rot", ShaderGraph::expand_type(ShaderGraph::T_Float));
|
||||||
|
l.track.uniforms["rot"].second = ShaderGraph::T_Map<ShaderGraph::T_Float>::type(.0f);
|
||||||
|
|
||||||
|
l.track.add_uniform("scale", ShaderGraph::expand_type(ShaderGraph::T_XY));
|
||||||
|
l.track.uniforms["scale"].second = ShaderGraph::T_Map<ShaderGraph::T_XY>::type(1.0f, 1.0f);
|
||||||
|
|
||||||
|
l.track.add_uniform("translate", ShaderGraph::expand_type(ShaderGraph::T_XY));
|
||||||
|
l.track.uniforms["translate"].second = ShaderGraph::T_Map<ShaderGraph::T_XY>::type(.0f, .0f);
|
||||||
|
|
||||||
|
l.track.shader = "void main() {\n"
|
||||||
|
"\tvec2 uv = vec2(v_texcoord0.x, 1.0f - v_texcoord0.y) - .5f;\n"
|
||||||
|
"\tuv = uv - translate;\n"
|
||||||
|
"\tuv = uv * aspect_ratio;\n"
|
||||||
|
"\tuv = vec2(cos(rot)*uv.x - sin(rot)*uv.y, sin(rot)*uv.x + cos(rot)*uv.y);\n"
|
||||||
|
"\tuv = uv / scale;\n"
|
||||||
|
"\tuv = uv + .5f;\n\n"
|
||||||
|
"\tvec4 tx = texture2D(s_texColor, uv);\n"
|
||||||
|
"\tgl_FragColor = vec4(tx.rgb, tx.a * opacity);\n"
|
||||||
|
"}\n";
|
||||||
|
l.track.compile();
|
||||||
|
s.layers.push_back(std::move(l));
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::InputText("##LayerName", &name);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (no_selection) ImGui::BeginDisabled();
|
||||||
|
if (ImGui::Button("Delete Layer")) {
|
||||||
|
if (s.selected.contains(s.active))
|
||||||
|
s.active = -1;
|
||||||
|
u32 deleted = 0, before_active = 0;
|
||||||
|
const u32 sz = s.layers.size();
|
||||||
|
for (u32 i = 0; i < sz; i++) {
|
||||||
|
if (s.selected.contains(i)) {
|
||||||
|
s.layers[i - deleted].track.clear();
|
||||||
|
s.layers.erase(s.layers.begin() + i - deleted);
|
||||||
|
s.disabled.erase(i);
|
||||||
|
if (static_cast<i32>(i) < s.active)
|
||||||
|
before_active++;
|
||||||
|
deleted++;
|
||||||
|
}
|
||||||
|
else if (s.disabled.contains(i)) {
|
||||||
|
s.disabled.erase(i);
|
||||||
|
s.disabled.insert(i - deleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.selected.clear();
|
||||||
|
s.active -= static_cast<i32>(before_active);
|
||||||
|
}
|
||||||
|
if (no_selection) ImGui::EndDisabled();
|
||||||
|
|
||||||
|
if (ImGui::BeginTable("Layers", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
|
||||||
|
ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort);
|
||||||
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort);
|
||||||
|
ImGui::TableSetupColumn("Blending", ImGuiTableColumnFlags_NoSort, 100.0f);
|
||||||
|
ImGui::TableSetupColumn("##EnableCol", ImGuiTableColumnFlags_NoSort);
|
||||||
|
ImGui::TableSetupScrollFreeze(1, 1);
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
|
i32 move_from = -1, move_to = -1; bool after{};
|
||||||
|
for (u32 i = 0; i < s.layers.size(); i++) {
|
||||||
|
const bool selected = s.selected.contains(i), disabled = s.disabled.contains(i);
|
||||||
|
ImGui::PushID(static_cast<i32>(i));
|
||||||
|
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height);
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImGui::Text("%u", i);
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(1);
|
||||||
|
if (ImGui::Selectable(s.layers[i].name.c_str(), selected,
|
||||||
|
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap,
|
||||||
|
ImVec2(0, row_height))) {
|
||||||
|
const auto& io = ImGui::GetIO();
|
||||||
|
if (io.KeyCtrl) {
|
||||||
|
if (selected)
|
||||||
|
s.selected.erase(i);
|
||||||
|
else
|
||||||
|
s.selected.insert(i);
|
||||||
|
}
|
||||||
|
else if (io.KeyShift) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
s.selected.clear();
|
||||||
|
if (!selected)
|
||||||
|
s.selected.insert(i);
|
||||||
|
}
|
||||||
|
s.active = selected ? -1 : static_cast<i32>(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto source_flags = ImGuiDragDropFlags_SourceNoDisableHover | ImGuiDragDropFlags_SourceNoHoldToOpenOthers;
|
||||||
|
if (!no_selection) source_flags |= ImGuiDragDropFlags_SourceNoPreviewTooltip;
|
||||||
|
if ((no_selection || s.selected.contains(i)) && ImGui::BeginDragDropSource(source_flags)) {
|
||||||
|
if (s.selected.empty()) ImGui::Text("Moving From #%u %s", i, s.layers[i].name.c_str());
|
||||||
|
ImGui::SetDragDropPayload("DND_DEMO_NAME", &i, sizeof(u32));
|
||||||
|
ImGui::EndDragDropSource();
|
||||||
|
}
|
||||||
|
if ((no_selection || !s.selected.contains(i)) && ImGui::BeginDragDropTarget()) {
|
||||||
|
if (!no_selection) {
|
||||||
|
after = ImGui::GetMousePos().y - ImGui::GetWindowPos().y > ImGui::GetCursorPosY() - row_height / 2.0f;
|
||||||
|
if (after)
|
||||||
|
ImGui::SetTooltip("After #%u %s", i, s.layers[i].name.c_str());
|
||||||
|
else
|
||||||
|
ImGui::SetTooltip("Before #%u %s", i, s.layers[i].name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("DND_DEMO_NAME")) {
|
||||||
|
move_from = *(const int*)payload->Data;
|
||||||
|
move_to = i;
|
||||||
|
}
|
||||||
|
ImGui::EndDragDropTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(2);
|
||||||
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||||
|
if (ImGui::BeginCombo("##Blending", Graphics::BlendingToString[s.layers[i].mode])) {
|
||||||
|
for (i32 b = Graphics::K_V_AlphaOver; b != Graphics::K_V_Count; b++) {
|
||||||
|
const bool is_selected = (static_cast<Graphics::Blending>(b) == s.layers[i].mode);
|
||||||
|
if (ImGui::Selectable(Graphics::BlendingToString[b], is_selected))
|
||||||
|
s.layers[i].mode = static_cast<Graphics::Blending>(b);
|
||||||
|
if (is_selected)
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
ImGui::TableSetColumnIndex(3);
|
||||||
|
bool cp = !disabled;
|
||||||
|
ImGui::Checkbox("##Enable", &cp);
|
||||||
|
if (cp == disabled) {
|
||||||
|
if (disabled)
|
||||||
|
s.disabled.erase(i);
|
||||||
|
else
|
||||||
|
s.disabled.insert(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (move_from != -1 && move_to != -1) {
|
||||||
|
if (no_selection)
|
||||||
|
std::swap(s.layers[move_to], s.layers[move_from]);
|
||||||
|
|
||||||
|
else {
|
||||||
|
const u32 n = s.selected.size();
|
||||||
|
u32 inserted = 0;
|
||||||
|
u32 target = move_to + after, popped_before_target = 0;
|
||||||
|
for (auto it = s.selected.rbegin(); it != s.selected.rend(); it++, inserted++) {
|
||||||
|
u32 sel = *it;
|
||||||
|
if (sel > target) sel += inserted; // note that we iterate backwards, so cur index is always right if < target
|
||||||
|
Layer l = s.layers[sel];
|
||||||
|
s.layers.erase(s.layers.begin() + sel);
|
||||||
|
if (sel < move_to) popped_before_target++;
|
||||||
|
s.layers.emplace(s.layers.begin() + target - popped_before_target, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.selected.clear();
|
||||||
|
for (u32 j = 0; j < n; j++)
|
||||||
|
s.selected.insert(target - popped_before_target + j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Plugboard(CompState& s) {
|
||||||
|
if (ImGui::Begin("Plugboard", &draw_plugboard)) {
|
||||||
|
if (ImGui::BeginTable("Source", 1, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit, { 50.0f, -FLT_MIN })) {
|
||||||
|
|
||||||
|
ImGui::PushID(1);
|
||||||
|
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height);
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("Time");
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::InvisibleButton("Graph", ImVec2{200.0f, 200.0f})) {
|
||||||
|
Log("d");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Nodegraph(CompState& s) {
|
||||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||||
if (window->SkipItems) {
|
if (window->SkipItems) {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active == nullptr) {
|
|
||||||
ImGui::Text("No layer selected.");
|
|
||||||
ImGui::End();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
ImGui::Text("Inspecting layer #%u \"%s\"", active_index, active->name.c_str());
|
ImGui::Text("Inspecting layer #%u \"%s\"", active_index, active->name.c_str());
|
||||||
|
@ -323,56 +478,72 @@ namespace K::UI {
|
||||||
|
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}*/
|
}
|
||||||
|
|
||||||
void Shader(CompState& s) {
|
void Shader(CompState& s) {
|
||||||
if (ImGui::Begin("Shader", &draw_nodes)) {
|
if (ImGui::Begin("Shader", &draw_shader)) {
|
||||||
if (s.active == -1)
|
if (s.active == -1)
|
||||||
ImGui::Text("No active layer.");
|
ImGui::Text("No active layer.");
|
||||||
else {
|
else {
|
||||||
static int type_current = 0;
|
static int type_current = 0;
|
||||||
static String name{};
|
static String name{};
|
||||||
if (ImGui::Button("Add Uniform") && !name.empty()) {
|
if (ImGui::Button("Add Uniform") && !name.empty() && !isdigit(name[0]) && name[0] != 'f') {
|
||||||
s.layers[s.active].track.add_uniform(name, ShaderGraph::expand_type(static_cast<ShaderGraph::Type>(type_current)));
|
s.layers[s.active].track.add_uniform(name, ShaderGraph::expand_type(static_cast<ShaderGraph::Type>(type_current)));
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::InputText("##UniformName", &name);
|
struct TextFilters { static int VariableName(ImGuiInputTextCallbackData *data) {
|
||||||
|
return !(data->EventChar < 256 &&
|
||||||
|
((data->EventChar >= 'a' && data->EventChar <= 'z') ||
|
||||||
|
(data->EventChar >= 'A' && data->EventChar <= 'Z')||
|
||||||
|
(data->EventChar >= '0' && data->EventChar <= '9') ||
|
||||||
|
data->EventChar == '_'));
|
||||||
|
} };
|
||||||
|
ImGui::InputText("##UniformName", &name, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::VariableName);
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||||
ImGui::Combo("Type", &type_current, ShaderGraph::Type_To_Str, ShaderGraph::T_Count);
|
ImGui::Combo("Type", &type_current, ShaderGraph::Type_To_Str, ShaderGraph::T_Count);
|
||||||
|
|
||||||
if (ImGui::BeginTable("Uniforms", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
|
if (ImGui::BeginTable("Uniforms", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
|
||||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort);
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort);
|
||||||
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_NoSort);
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_NoSort);
|
||||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_NoSort);
|
ImGui::TableSetupColumn("Expose", ImGuiTableColumnFlags_NoSort);
|
||||||
|
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_NoSort, ImGui::GetContentRegionAvail().x / 1.7f);
|
||||||
ImGui::TableSetupColumn("##del", ImGuiTableColumnFlags_NoSort);
|
ImGui::TableSetupColumn("##del", ImGuiTableColumnFlags_NoSort);
|
||||||
ImGui::TableHeadersRow();
|
ImGui::TableHeadersRow();
|
||||||
|
i32 i = 0;
|
||||||
for (auto it = s.layers[s.active].track.uniforms.begin(); it != s.layers[s.active].track.uniforms.end();) {
|
for (auto it = s.layers[s.active].track.uniforms.begin(); it != s.layers[s.active].track.uniforms.end();) {
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
ImGui::PushID(i++);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("%s", it->first.c_str());
|
ImGui::Text("%s", it->first.c_str());
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("%s", ShaderGraph::Type_To_Str[it->second.second.index()]);
|
ImGui::Text("%s", ShaderGraph::Type_To_Str[it->second.second.index()]);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
bool temp{};
|
||||||
|
ImGui::Checkbox("##Expose", &temp);
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||||
std::visit([it](auto&& arg) {
|
std::visit([it](auto&& arg) {
|
||||||
using T = std::decay_t<decltype(arg)>;
|
using T = std::decay_t<decltype(arg)>;
|
||||||
if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_Float>::type>)
|
if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_Float>::type>)
|
||||||
ImGui::DragFloat(("##" + it->first).c_str(), &arg, 0.005f);
|
ImGui::DragFloat("##", &arg, 0.005f);
|
||||||
else if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_Int>::type>)
|
else if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_Int>::type>)
|
||||||
ImGui::InputInt(("##" + it->first).c_str(), &arg);
|
ImGui::DragInt("##", &arg);
|
||||||
else if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_RGBA>::type>)
|
else if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_RGBA>::type>)
|
||||||
ImGui::InputFloat4(("##" + it->first).c_str(), &arg.r);
|
ImGui::DragFloat4("##", &arg.r);
|
||||||
else if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_XY>::type>)
|
else if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_XY>::type>)
|
||||||
ImGui::InputFloat2(("##" + it->first).c_str(), &arg.x);
|
ImGui::DragFloat2("##", &arg.x);
|
||||||
else if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_XYZ>::type>)
|
else if constexpr (std::is_same_v<T, ShaderGraph::T_Map<ShaderGraph::T_XYZ>::type>)
|
||||||
ImGui::InputFloat3(("##" + it->first).c_str(), &arg.x);
|
ImGui::DragFloat3("##", &arg.x);
|
||||||
}, it->second.second);
|
}, it->second.second);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (ImGui::Button(("Delete Uniform##" + it->first).c_str())) {
|
if (ImGui::Button("X")) {
|
||||||
bgfx::destroy(it->second.first);
|
bgfx::destroy(it->second.first);
|
||||||
it = s.layers[s.active].track.uniforms.erase(it);
|
it = s.layers[s.active].track.uniforms.erase(it);
|
||||||
}
|
}
|
||||||
else it++;
|
else it++;
|
||||||
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
@ -385,18 +556,24 @@ namespace K::UI {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Draw(CompState& s) {
|
void Draw(u32 frame, CompState& s) {
|
||||||
ImGui_ImplSDL2_NewFrame();
|
ImGui_ImplSDL2_NewFrame();
|
||||||
ImGui_Implbgfx_NewFrame();
|
ImGui_Implbgfx_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
// ImGui::ShowDemoWindow();
|
ImGui::ShowDemoWindow();
|
||||||
static ImGuiStyle& style = ImGui::GetStyle();
|
static ImGuiStyle& style = ImGui::GetStyle();
|
||||||
style.GrabRounding = style.FrameRounding = 5.0f;
|
style.GrabRounding = style.FrameRounding = 5.0f;
|
||||||
MainMenuBar(s);
|
MainMenuBar(s);
|
||||||
if (draw_viewport) Viewport(s);
|
if (draw_viewport) Viewport(s);
|
||||||
if (draw_nodes) Shader(s);
|
if (draw_shader) Shader(s);
|
||||||
if (draw_sequencer) Sequencer(s);
|
if (draw_plugboard) Plugboard(s);
|
||||||
|
if (draw_comp) Composition(s);
|
||||||
|
|
||||||
|
if (save_called && ready_frame == frame) {
|
||||||
|
stbi_write_png("frame.png", s.width, s.height, 4, save_buffer, s.width * 4);
|
||||||
|
save_called = false;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
ImGui_Implbgfx_RenderDrawLists(ImGui::GetDrawData());
|
ImGui_Implbgfx_RenderDrawLists(ImGui::GetDrawData());
|
||||||
|
@ -406,17 +583,28 @@ namespace K::UI {
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||||
// io.Fonts->AddFontFromFileTTF("SourceHanSans-Regular.ttc", 17);
|
// io.Fonts->AddFontFromFileTTF("SourceHanSans-Regular.ttc", 17);
|
||||||
ImGui_Implbgfx_Init(K::Graphics::K_VIEW_TOP);
|
ImGui_Implbgfx_Init(K::Graphics::K_VIEW_TOP);
|
||||||
|
|
||||||
#if BX_PLATFORM_WINDOWS
|
switch (bgfx::getRendererType()) {
|
||||||
ImGui_ImplSDL2_InitForD3D(window);
|
case bgfx::RendererType::Noop:
|
||||||
#elif BX_PLATFORM_OSX
|
case bgfx::RendererType::Direct3D11:
|
||||||
ImGui_ImplSDL2_InitForMetal(window);
|
case bgfx::RendererType::Direct3D12:
|
||||||
#elif BX_PLATFORM_LINUX || BX_PLATFORM_EMSCRIPTEN
|
ImGui_ImplSDL2_InitForD3D(window);
|
||||||
ImGui_ImplSDL2_InitForVulkan(window);
|
break;
|
||||||
#endif
|
case bgfx::RendererType::Metal:
|
||||||
|
ImGui_ImplSDL2_InitForMetal(window);
|
||||||
|
break;
|
||||||
|
case bgfx::RendererType::OpenGL:
|
||||||
|
ImGui_ImplSDL2_InitForOpenGL(window, nullptr);
|
||||||
|
break;
|
||||||
|
case bgfx::RendererType::Vulkan:
|
||||||
|
ImGui_ImplSDL2_InitForVulkan(window);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LogError("Unsupported Renderer");
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown(CompState& s) {
|
void Shutdown(CompState& s) {
|
||||||
|
|
1
Keishiki/ext/ImNodeFlow
Submodule
1
Keishiki/ext/ImNodeFlow
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 3e4ed6e5b51cc9a874480b94f112d5965b0412b0
|
|
@ -1 +1 @@
|
||||||
Subproject commit aa5a6098ee24ca30b3e0a180282619777e95fc67
|
Subproject commit b39fc84f8919b87c9d79f6ffaf43efb61d2beb3d
|
|
@ -7,7 +7,9 @@
|
||||||
|
|
||||||
namespace K::Graphics {
|
namespace K::Graphics {
|
||||||
enum VIEW_ID {
|
enum VIEW_ID {
|
||||||
|
K_VIEW_BG,
|
||||||
K_VIEW_COMP_COMPOSITE,
|
K_VIEW_COMP_COMPOSITE,
|
||||||
|
K_VIEW_COMP_SAVE,
|
||||||
K_VIEW_DRAW,
|
K_VIEW_DRAW,
|
||||||
K_VIEW_TOP,
|
K_VIEW_TOP,
|
||||||
};
|
};
|
||||||
|
@ -22,7 +24,7 @@ namespace K::Graphics {
|
||||||
u8 *buffer;
|
u8 *buffer;
|
||||||
bgfx::TextureHandle tx;
|
bgfx::TextureHandle tx;
|
||||||
};
|
};
|
||||||
ImageTexture *GetImageTextureFromFile(const std::string& file);
|
ImageTexture *GetImageTextureFromFile(const std::string& file); // Caller not responsible for freeing
|
||||||
|
|
||||||
void DrawTextureWithTransform(u32 view_id, const bgfx::TextureHandle& tex, f32 mtx[16], u64 state, const bgfx::ProgramHandle& pg);
|
void DrawTextureWithTransform(u32 view_id, const bgfx::TextureHandle& tex, f32 mtx[16], u64 state, const bgfx::ProgramHandle& pg);
|
||||||
void DrawTexture(u32 view_id, const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h, u64 state, const bgfx::ProgramHandle& pg);
|
void DrawTexture(u32 view_id, const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h, u64 state, const bgfx::ProgramHandle& pg);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "VisualTrack.h"
|
#include "VisualTrack.h"
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace K {
|
namespace K {
|
||||||
bool Init();
|
bool Init();
|
||||||
|
@ -12,7 +14,6 @@ namespace K {
|
||||||
struct Layer {
|
struct Layer {
|
||||||
VisualTrack track;
|
VisualTrack track;
|
||||||
String name;
|
String name;
|
||||||
bool enabled, selected;
|
|
||||||
Graphics::Blending mode;
|
Graphics::Blending mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,6 +23,8 @@ namespace K {
|
||||||
u32 fps;
|
u32 fps;
|
||||||
u32 width, height;
|
u32 width, height;
|
||||||
Vector<Layer> layers;
|
Vector<Layer> layers;
|
||||||
i32 active = -1;
|
i32 active = -1; // index for layers
|
||||||
|
std::set<u32> selected; // indices for layers
|
||||||
|
std::unordered_set<u32> disabled; // indices for layers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Graphics.h"
|
#include "Graphics.h"
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "Graphics.h"
|
#include "Graphics.h"
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
namespace K::UI {
|
namespace K::UI {
|
||||||
void Init(SDL_Window *window);
|
void Init(SDL_Window *window);
|
||||||
void Draw(CompState& s);
|
void Draw(u32 frame, CompState& s);
|
||||||
void Shutdown(CompState& s);
|
void Shutdown(CompState& s);
|
||||||
void SetupViewport(CompState& s);
|
void SetupViewport(CompState& s);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,7 @@ namespace K {
|
||||||
struct VisualTrack {
|
struct VisualTrack {
|
||||||
ShaderGraph::ShaderGraph tree;
|
ShaderGraph::ShaderGraph tree;
|
||||||
bgfx::ProgramHandle pg = BGFX_INVALID_HANDLE;
|
bgfx::ProgramHandle pg = BGFX_INVALID_HANDLE;
|
||||||
String shader = "void main() {\n"
|
String shader;
|
||||||
"\tvec2 uv = vec2(v_texcoord0.x, 1.0f - v_texcoord0.y);\n"
|
|
||||||
"\tvec4 tx = texture2D(s_texColor, uv);\n"
|
|
||||||
"\tgl_FragColor = vec4(tx.rgb, tx.a);\n"
|
|
||||||
"}\n";
|
|
||||||
Dict<String, std::pair<bgfx::UniformHandle, ShaderGraph::T_Map<ShaderGraph::T_Count>::type>> uniforms;
|
Dict<String, std::pair<bgfx::UniformHandle, ShaderGraph::T_Map<ShaderGraph::T_Count>::type>> uniforms;
|
||||||
// Vector<String> samplers;
|
// Vector<String> samplers;
|
||||||
bgfx::TextureHandle get_frame(bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16], f32 view[16], f32 transform[16]) const {
|
bgfx::TextureHandle get_frame(bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16], f32 view[16], f32 transform[16]) const {
|
||||||
|
@ -36,7 +32,7 @@ namespace K {
|
||||||
|
|
||||||
bgfx::setViewTransform(K::Graphics::K_VIEW_COMP_COMPOSITE, view, proj);
|
bgfx::setViewTransform(K::Graphics::K_VIEW_COMP_COMPOSITE, view, proj);
|
||||||
bgfx::setViewRect(K::Graphics::K_VIEW_COMP_COMPOSITE, 0, 0, w, h);
|
bgfx::setViewRect(K::Graphics::K_VIEW_COMP_COMPOSITE, 0, 0, w, h);
|
||||||
// TODO Find a better way to pack...
|
|
||||||
for (auto& [_name, u] : uniforms) {
|
for (auto& [_name, u] : uniforms) {
|
||||||
f32 pack[4]{};
|
f32 pack[4]{};
|
||||||
std::visit([&pack](auto&& arg) {
|
std::visit([&pack](auto&& arg) {
|
||||||
|
@ -69,7 +65,7 @@ namespace K {
|
||||||
}
|
}
|
||||||
void add_uniform(const String& s, ShaderGraph::T_Map<ShaderGraph::T_Count>::type&& val) {
|
void add_uniform(const String& s, ShaderGraph::T_Map<ShaderGraph::T_Count>::type&& val) {
|
||||||
if (!uniforms.contains(s))
|
if (!uniforms.contains(s))
|
||||||
uniforms.emplace(s, std::make_pair(bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), std::move(val)));
|
uniforms.emplace(s, std::make_pair(bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val));
|
||||||
}
|
}
|
||||||
void compile() {
|
void compile() {
|
||||||
std::ofstream f("temp.frag");
|
std::ofstream f("temp.frag");
|
||||||
|
@ -143,6 +139,15 @@ namespace K {
|
||||||
else
|
else
|
||||||
Log("User shader compilation failed");
|
Log("User shader compilation failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
if (bgfx::isValid(pg))
|
||||||
|
bgfx::destroy(pg);
|
||||||
|
for (auto& uniform : uniforms) {
|
||||||
|
bgfx::destroy(uniform.second.first);
|
||||||
|
}
|
||||||
|
uniforms.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Composite();
|
void Composite();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb
|
/* stb_image - v2.29 - public domain image loader - http://nothings.org/stb
|
||||||
no warranty implied; use at your own risk
|
no warranty implied; use at your own risk
|
||||||
|
|
||||||
Do this:
|
Do this:
|
||||||
|
@ -48,6 +48,8 @@ LICENSE
|
||||||
|
|
||||||
RECENT REVISION HISTORY:
|
RECENT REVISION HISTORY:
|
||||||
|
|
||||||
|
2.29 (2023-05-xx) optimizations
|
||||||
|
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
|
||||||
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
|
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
|
||||||
2.26 (2020-07-13) many minor fixes
|
2.26 (2020-07-13) many minor fixes
|
||||||
2.25 (2020-02-02) fix warnings
|
2.25 (2020-02-02) fix warnings
|
||||||
|
@ -108,7 +110,7 @@ RECENT REVISION HISTORY:
|
||||||
Cass Everitt Ryamond Barbiero github:grim210
|
Cass Everitt Ryamond Barbiero github:grim210
|
||||||
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
|
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
|
||||||
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
|
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
|
||||||
Josh Tobin Matthew Gregan github:poppolopoppo
|
Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
|
||||||
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
|
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
|
||||||
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
|
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
|
||||||
Brad Weinberger Matvey Cherevko github:mosra
|
Brad Weinberger Matvey Cherevko github:mosra
|
||||||
|
@ -140,7 +142,7 @@ RECENT REVISION HISTORY:
|
||||||
// // ... x = width, y = height, n = # 8-bit components per pixel ...
|
// // ... x = width, y = height, n = # 8-bit components per pixel ...
|
||||||
// // ... replace '0' with '1'..'4' to force that many components per pixel
|
// // ... replace '0' with '1'..'4' to force that many components per pixel
|
||||||
// // ... but 'n' will always be the number that it would have been if you said 0
|
// // ... but 'n' will always be the number that it would have been if you said 0
|
||||||
// stbi_image_free(data)
|
// stbi_image_free(data);
|
||||||
//
|
//
|
||||||
// Standard parameters:
|
// Standard parameters:
|
||||||
// int *x -- outputs image width in pixels
|
// int *x -- outputs image width in pixels
|
||||||
|
@ -635,7 +637,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#if defined(_MSC_VER) || defined(__SYMBIAN32__)
|
||||||
typedef unsigned short stbi__uint16;
|
typedef unsigned short stbi__uint16;
|
||||||
typedef signed short stbi__int16;
|
typedef signed short stbi__int16;
|
||||||
typedef unsigned int stbi__uint32;
|
typedef unsigned int stbi__uint32;
|
||||||
|
@ -1063,6 +1065,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.
|
||||||
|
static int stbi__addints_valid(int a, int b)
|
||||||
|
{
|
||||||
|
if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow
|
||||||
|
if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.
|
||||||
|
return a <= INT_MAX - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns 1 if the product of two ints fits in a signed short, 0 on overflow.
|
||||||
|
static int stbi__mul2shorts_valid(int a, int b)
|
||||||
|
{
|
||||||
|
if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
|
||||||
|
if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
|
||||||
|
if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN
|
||||||
|
return a >= SHRT_MIN / b;
|
||||||
|
}
|
||||||
|
|
||||||
// stbi__err - error
|
// stbi__err - error
|
||||||
// stbi__errpf - error returning pointer to float
|
// stbi__errpf - error returning pointer to float
|
||||||
// stbi__errpuc - error returning pointer to unsigned char
|
// stbi__errpuc - error returning pointer to unsigned char
|
||||||
|
@ -1985,9 +2004,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count)
|
||||||
int i,j,k=0;
|
int i,j,k=0;
|
||||||
unsigned int code;
|
unsigned int code;
|
||||||
// build size list for each symbol (from JPEG spec)
|
// build size list for each symbol (from JPEG spec)
|
||||||
for (i=0; i < 16; ++i)
|
for (i=0; i < 16; ++i) {
|
||||||
for (j=0; j < count[i]; ++j)
|
for (j=0; j < count[i]; ++j) {
|
||||||
h->size[k++] = (stbi_uc) (i+1);
|
h->size[k++] = (stbi_uc) (i+1);
|
||||||
|
if(k >= 257) return stbi__err("bad size list","Corrupt JPEG");
|
||||||
|
}
|
||||||
|
}
|
||||||
h->size[k] = 0;
|
h->size[k] = 0;
|
||||||
|
|
||||||
// compute actual symbols (from jpeg spec)
|
// compute actual symbols (from jpeg spec)
|
||||||
|
@ -2112,6 +2134,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
|
||||||
|
|
||||||
// convert the huffman code to the symbol id
|
// convert the huffman code to the symbol id
|
||||||
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
|
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
|
||||||
|
if(c < 0 || c >= 256) // symbol id out of bounds!
|
||||||
|
return -1;
|
||||||
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
|
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
|
||||||
|
|
||||||
// convert the id to a symbol
|
// convert the id to a symbol
|
||||||
|
@ -2130,6 +2154,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
|
||||||
unsigned int k;
|
unsigned int k;
|
||||||
int sgn;
|
int sgn;
|
||||||
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
||||||
|
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
|
||||||
|
|
||||||
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
|
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
|
||||||
k = stbi_lrot(j->code_buffer, n);
|
k = stbi_lrot(j->code_buffer, n);
|
||||||
|
@ -2144,6 +2169,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
|
||||||
{
|
{
|
||||||
unsigned int k;
|
unsigned int k;
|
||||||
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
||||||
|
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
|
||||||
k = stbi_lrot(j->code_buffer, n);
|
k = stbi_lrot(j->code_buffer, n);
|
||||||
j->code_buffer = k & ~stbi__bmask[n];
|
j->code_buffer = k & ~stbi__bmask[n];
|
||||||
k &= stbi__bmask[n];
|
k &= stbi__bmask[n];
|
||||||
|
@ -2155,6 +2181,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
|
||||||
{
|
{
|
||||||
unsigned int k;
|
unsigned int k;
|
||||||
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
|
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
|
||||||
|
if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing
|
||||||
k = j->code_buffer;
|
k = j->code_buffer;
|
||||||
j->code_buffer <<= 1;
|
j->code_buffer <<= 1;
|
||||||
--j->code_bits;
|
--j->code_bits;
|
||||||
|
@ -2192,8 +2219,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
|
||||||
memset(data,0,64*sizeof(data[0]));
|
memset(data,0,64*sizeof(data[0]));
|
||||||
|
|
||||||
diff = t ? stbi__extend_receive(j, t) : 0;
|
diff = t ? stbi__extend_receive(j, t) : 0;
|
||||||
|
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG");
|
||||||
dc = j->img_comp[b].dc_pred + diff;
|
dc = j->img_comp[b].dc_pred + diff;
|
||||||
j->img_comp[b].dc_pred = dc;
|
j->img_comp[b].dc_pred = dc;
|
||||||
|
if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||||
data[0] = (short) (dc * dequant[0]);
|
data[0] = (short) (dc * dequant[0]);
|
||||||
|
|
||||||
// decode AC components, see JPEG spec
|
// decode AC components, see JPEG spec
|
||||||
|
@ -2207,6 +2236,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
|
||||||
if (r) { // fast-AC path
|
if (r) { // fast-AC path
|
||||||
k += (r >> 4) & 15; // run
|
k += (r >> 4) & 15; // run
|
||||||
s = r & 15; // combined length
|
s = r & 15; // combined length
|
||||||
|
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
|
||||||
j->code_buffer <<= s;
|
j->code_buffer <<= s;
|
||||||
j->code_bits -= s;
|
j->code_bits -= s;
|
||||||
// decode into unzigzag'd location
|
// decode into unzigzag'd location
|
||||||
|
@ -2246,8 +2276,10 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
|
||||||
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||||
diff = t ? stbi__extend_receive(j, t) : 0;
|
diff = t ? stbi__extend_receive(j, t) : 0;
|
||||||
|
|
||||||
|
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG");
|
||||||
dc = j->img_comp[b].dc_pred + diff;
|
dc = j->img_comp[b].dc_pred + diff;
|
||||||
j->img_comp[b].dc_pred = dc;
|
j->img_comp[b].dc_pred = dc;
|
||||||
|
if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||||
data[0] = (short) (dc * (1 << j->succ_low));
|
data[0] = (short) (dc * (1 << j->succ_low));
|
||||||
} else {
|
} else {
|
||||||
// refinement scan for DC coefficient
|
// refinement scan for DC coefficient
|
||||||
|
@ -2282,6 +2314,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
|
||||||
if (r) { // fast-AC path
|
if (r) { // fast-AC path
|
||||||
k += (r >> 4) & 15; // run
|
k += (r >> 4) & 15; // run
|
||||||
s = r & 15; // combined length
|
s = r & 15; // combined length
|
||||||
|
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
|
||||||
j->code_buffer <<= s;
|
j->code_buffer <<= s;
|
||||||
j->code_bits -= s;
|
j->code_bits -= s;
|
||||||
zig = stbi__jpeg_dezigzag[k++];
|
zig = stbi__jpeg_dezigzag[k++];
|
||||||
|
@ -3102,6 +3135,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
|
||||||
sizes[i] = stbi__get8(z->s);
|
sizes[i] = stbi__get8(z->s);
|
||||||
n += sizes[i];
|
n += sizes[i];
|
||||||
}
|
}
|
||||||
|
if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values!
|
||||||
L -= 17;
|
L -= 17;
|
||||||
if (tc == 0) {
|
if (tc == 0) {
|
||||||
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
|
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
|
||||||
|
@ -3351,6 +3385,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
|
||||||
|
{
|
||||||
|
// some JPEGs have junk at end, skip over it but if we find what looks
|
||||||
|
// like a valid marker, resume there
|
||||||
|
while (!stbi__at_eof(j->s)) {
|
||||||
|
stbi_uc x = stbi__get8(j->s);
|
||||||
|
while (x == 0xff) { // might be a marker
|
||||||
|
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
|
||||||
|
x = stbi__get8(j->s);
|
||||||
|
if (x != 0x00 && x != 0xff) {
|
||||||
|
// not a stuffed zero or lead-in to another marker, looks
|
||||||
|
// like an actual marker, return it
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
// stuffed zero has x=0 now which ends the loop, meaning we go
|
||||||
|
// back to regular scan loop.
|
||||||
|
// repeated 0xff keeps trying to read the next byte of the marker.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return STBI__MARKER_none;
|
||||||
|
}
|
||||||
|
|
||||||
// decode image to YCbCr format
|
// decode image to YCbCr format
|
||||||
static int stbi__decode_jpeg_image(stbi__jpeg *j)
|
static int stbi__decode_jpeg_image(stbi__jpeg *j)
|
||||||
{
|
{
|
||||||
|
@ -3367,25 +3423,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j)
|
||||||
if (!stbi__process_scan_header(j)) return 0;
|
if (!stbi__process_scan_header(j)) return 0;
|
||||||
if (!stbi__parse_entropy_coded_data(j)) return 0;
|
if (!stbi__parse_entropy_coded_data(j)) return 0;
|
||||||
if (j->marker == STBI__MARKER_none ) {
|
if (j->marker == STBI__MARKER_none ) {
|
||||||
// handle 0s at the end of image data from IP Kamera 9060
|
j->marker = stbi__skip_jpeg_junk_at_end(j);
|
||||||
while (!stbi__at_eof(j->s)) {
|
|
||||||
int x = stbi__get8(j->s);
|
|
||||||
if (x == 255) {
|
|
||||||
j->marker = stbi__get8(j->s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
|
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
|
||||||
}
|
}
|
||||||
|
m = stbi__get_marker(j);
|
||||||
|
if (STBI__RESTART(m))
|
||||||
|
m = stbi__get_marker(j);
|
||||||
} else if (stbi__DNL(m)) {
|
} else if (stbi__DNL(m)) {
|
||||||
int Ld = stbi__get16be(j->s);
|
int Ld = stbi__get16be(j->s);
|
||||||
stbi__uint32 NL = stbi__get16be(j->s);
|
stbi__uint32 NL = stbi__get16be(j->s);
|
||||||
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
|
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
|
||||||
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
|
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
|
||||||
|
m = stbi__get_marker(j);
|
||||||
} else {
|
} else {
|
||||||
if (!stbi__process_marker(j, m)) return 0;
|
if (!stbi__process_marker(j, m)) return 1;
|
||||||
|
m = stbi__get_marker(j);
|
||||||
}
|
}
|
||||||
m = stbi__get_marker(j);
|
|
||||||
}
|
}
|
||||||
if (j->progressive)
|
if (j->progressive)
|
||||||
stbi__jpeg_finish(j);
|
stbi__jpeg_finish(j);
|
||||||
|
@ -3976,6 +4029,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re
|
||||||
unsigned char* result;
|
unsigned char* result;
|
||||||
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
|
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
|
||||||
if (!j) return stbi__errpuc("outofmem", "Out of memory");
|
if (!j) return stbi__errpuc("outofmem", "Out of memory");
|
||||||
|
memset(j, 0, sizeof(stbi__jpeg));
|
||||||
STBI_NOTUSED(ri);
|
STBI_NOTUSED(ri);
|
||||||
j->s = s;
|
j->s = s;
|
||||||
stbi__setup_jpeg(j);
|
stbi__setup_jpeg(j);
|
||||||
|
@ -3989,6 +4043,7 @@ static int stbi__jpeg_test(stbi__context *s)
|
||||||
int r;
|
int r;
|
||||||
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
|
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
|
||||||
if (!j) return stbi__err("outofmem", "Out of memory");
|
if (!j) return stbi__err("outofmem", "Out of memory");
|
||||||
|
memset(j, 0, sizeof(stbi__jpeg));
|
||||||
j->s = s;
|
j->s = s;
|
||||||
stbi__setup_jpeg(j);
|
stbi__setup_jpeg(j);
|
||||||
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
|
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
|
||||||
|
@ -4014,6 +4069,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
|
||||||
int result;
|
int result;
|
||||||
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
|
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
|
||||||
if (!j) return stbi__err("outofmem", "Out of memory");
|
if (!j) return stbi__err("outofmem", "Out of memory");
|
||||||
|
memset(j, 0, sizeof(stbi__jpeg));
|
||||||
j->s = s;
|
j->s = s;
|
||||||
result = stbi__jpeg_info_raw(j, x, y, comp);
|
result = stbi__jpeg_info_raw(j, x, y, comp);
|
||||||
STBI_FREE(j);
|
STBI_FREE(j);
|
||||||
|
@ -4121,6 +4177,7 @@ typedef struct
|
||||||
{
|
{
|
||||||
stbi_uc *zbuffer, *zbuffer_end;
|
stbi_uc *zbuffer, *zbuffer_end;
|
||||||
int num_bits;
|
int num_bits;
|
||||||
|
int hit_zeof_once;
|
||||||
stbi__uint32 code_buffer;
|
stbi__uint32 code_buffer;
|
||||||
|
|
||||||
char *zout;
|
char *zout;
|
||||||
|
@ -4187,9 +4244,20 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
|
||||||
int b,s;
|
int b,s;
|
||||||
if (a->num_bits < 16) {
|
if (a->num_bits < 16) {
|
||||||
if (stbi__zeof(a)) {
|
if (stbi__zeof(a)) {
|
||||||
return -1; /* report error for unexpected end of data. */
|
if (!a->hit_zeof_once) {
|
||||||
|
// This is the first time we hit eof, insert 16 extra padding btis
|
||||||
|
// to allow us to keep going; if we actually consume any of them
|
||||||
|
// though, that is invalid data. This is caught later.
|
||||||
|
a->hit_zeof_once = 1;
|
||||||
|
a->num_bits += 16; // add 16 implicit zero bits
|
||||||
|
} else {
|
||||||
|
// We already inserted our extra 16 padding bits and are again
|
||||||
|
// out, this stream is actually prematurely terminated.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stbi__fill_bits(a);
|
||||||
}
|
}
|
||||||
stbi__fill_bits(a);
|
|
||||||
}
|
}
|
||||||
b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
|
b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
|
||||||
if (b) {
|
if (b) {
|
||||||
|
@ -4254,17 +4322,25 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
|
||||||
int len,dist;
|
int len,dist;
|
||||||
if (z == 256) {
|
if (z == 256) {
|
||||||
a->zout = zout;
|
a->zout = zout;
|
||||||
|
if (a->hit_zeof_once && a->num_bits < 16) {
|
||||||
|
// The first time we hit zeof, we inserted 16 extra zero bits into our bit
|
||||||
|
// buffer so the decoder can just do its speculative decoding. But if we
|
||||||
|
// actually consumed any of those bits (which is the case when num_bits < 16),
|
||||||
|
// the stream actually read past the end so it is malformed.
|
||||||
|
return stbi__err("unexpected end","Corrupt PNG");
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
|
||||||
z -= 257;
|
z -= 257;
|
||||||
len = stbi__zlength_base[z];
|
len = stbi__zlength_base[z];
|
||||||
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
|
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
|
||||||
z = stbi__zhuffman_decode(a, &a->z_distance);
|
z = stbi__zhuffman_decode(a, &a->z_distance);
|
||||||
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
|
if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data
|
||||||
dist = stbi__zdist_base[z];
|
dist = stbi__zdist_base[z];
|
||||||
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
|
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
|
||||||
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
|
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
|
||||||
if (zout + len > a->zout_end) {
|
if (len > a->zout_end - zout) {
|
||||||
if (!stbi__zexpand(a, zout, len)) return 0;
|
if (!stbi__zexpand(a, zout, len)) return 0;
|
||||||
zout = a->zout;
|
zout = a->zout;
|
||||||
}
|
}
|
||||||
|
@ -4408,6 +4484,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
|
||||||
if (!stbi__parse_zlib_header(a)) return 0;
|
if (!stbi__parse_zlib_header(a)) return 0;
|
||||||
a->num_bits = 0;
|
a->num_bits = 0;
|
||||||
a->code_buffer = 0;
|
a->code_buffer = 0;
|
||||||
|
a->hit_zeof_once = 0;
|
||||||
do {
|
do {
|
||||||
final = stbi__zreceive(a,1);
|
final = stbi__zreceive(a,1);
|
||||||
type = stbi__zreceive(a,2);
|
type = stbi__zreceive(a,2);
|
||||||
|
@ -4563,9 +4640,8 @@ enum {
|
||||||
STBI__F_up=2,
|
STBI__F_up=2,
|
||||||
STBI__F_avg=3,
|
STBI__F_avg=3,
|
||||||
STBI__F_paeth=4,
|
STBI__F_paeth=4,
|
||||||
// synthetic filters used for first scanline to avoid needing a dummy row of 0s
|
// synthetic filter used for first scanline to avoid needing a dummy row of 0s
|
||||||
STBI__F_avg_first,
|
STBI__F_avg_first
|
||||||
STBI__F_paeth_first
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static stbi_uc first_row_filter[5] =
|
static stbi_uc first_row_filter[5] =
|
||||||
|
@ -4574,29 +4650,56 @@ static stbi_uc first_row_filter[5] =
|
||||||
STBI__F_sub,
|
STBI__F_sub,
|
||||||
STBI__F_none,
|
STBI__F_none,
|
||||||
STBI__F_avg_first,
|
STBI__F_avg_first,
|
||||||
STBI__F_paeth_first
|
STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub
|
||||||
};
|
};
|
||||||
|
|
||||||
static int stbi__paeth(int a, int b, int c)
|
static int stbi__paeth(int a, int b, int c)
|
||||||
{
|
{
|
||||||
int p = a + b - c;
|
// This formulation looks very different from the reference in the PNG spec, but is
|
||||||
int pa = abs(p-a);
|
// actually equivalent and has favorable data dependencies and admits straightforward
|
||||||
int pb = abs(p-b);
|
// generation of branch-free code, which helps performance significantly.
|
||||||
int pc = abs(p-c);
|
int thresh = c*3 - (a + b);
|
||||||
if (pa <= pb && pa <= pc) return a;
|
int lo = a < b ? a : b;
|
||||||
if (pb <= pc) return b;
|
int hi = a < b ? b : a;
|
||||||
return c;
|
int t0 = (hi <= thresh) ? lo : c;
|
||||||
|
int t1 = (thresh <= lo) ? hi : t0;
|
||||||
|
return t1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
|
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
|
||||||
|
|
||||||
|
// adds an extra all-255 alpha channel
|
||||||
|
// dest == src is legal
|
||||||
|
// img_n must be 1 or 3
|
||||||
|
static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
// must process data backwards since we allow dest==src
|
||||||
|
if (img_n == 1) {
|
||||||
|
for (i=x-1; i >= 0; --i) {
|
||||||
|
dest[i*2+1] = 255;
|
||||||
|
dest[i*2+0] = src[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
STBI_ASSERT(img_n == 3);
|
||||||
|
for (i=x-1; i >= 0; --i) {
|
||||||
|
dest[i*4+3] = 255;
|
||||||
|
dest[i*4+2] = src[i*3+2];
|
||||||
|
dest[i*4+1] = src[i*3+1];
|
||||||
|
dest[i*4+0] = src[i*3+0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create the png data from post-deflated data
|
// create the png data from post-deflated data
|
||||||
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
|
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
|
||||||
{
|
{
|
||||||
int bytes = (depth == 16? 2 : 1);
|
int bytes = (depth == 16 ? 2 : 1);
|
||||||
stbi__context *s = a->s;
|
stbi__context *s = a->s;
|
||||||
stbi__uint32 i,j,stride = x*out_n*bytes;
|
stbi__uint32 i,j,stride = x*out_n*bytes;
|
||||||
stbi__uint32 img_len, img_width_bytes;
|
stbi__uint32 img_len, img_width_bytes;
|
||||||
|
stbi_uc *filter_buf;
|
||||||
|
int all_ok = 1;
|
||||||
int k;
|
int k;
|
||||||
int img_n = s->img_n; // copy it into a local for later
|
int img_n = s->img_n; // copy it into a local for later
|
||||||
|
|
||||||
|
@ -4608,8 +4711,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
||||||
a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
|
a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
|
||||||
if (!a->out) return stbi__err("outofmem", "Out of memory");
|
if (!a->out) return stbi__err("outofmem", "Out of memory");
|
||||||
|
|
||||||
|
// note: error exits here don't need to clean up a->out individually,
|
||||||
|
// stbi__do_png always does on error.
|
||||||
if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
|
if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
|
||||||
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
|
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
|
||||||
|
if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG");
|
||||||
img_len = (img_width_bytes + 1) * y;
|
img_len = (img_width_bytes + 1) * y;
|
||||||
|
|
||||||
// we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
|
// we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
|
||||||
|
@ -4617,189 +4723,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
||||||
// so just check for raw_len < img_len always.
|
// so just check for raw_len < img_len always.
|
||||||
if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
|
if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
|
||||||
|
|
||||||
|
// Allocate two scan lines worth of filter workspace buffer.
|
||||||
|
filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0);
|
||||||
|
if (!filter_buf) return stbi__err("outofmem", "Out of memory");
|
||||||
|
|
||||||
|
// Filtering for low-bit-depth images
|
||||||
|
if (depth < 8) {
|
||||||
|
filter_bytes = 1;
|
||||||
|
width = img_width_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
for (j=0; j < y; ++j) {
|
for (j=0; j < y; ++j) {
|
||||||
stbi_uc *cur = a->out + stride*j;
|
// cur/prior filter buffers alternate
|
||||||
stbi_uc *prior;
|
stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes;
|
||||||
|
stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes;
|
||||||
|
stbi_uc *dest = a->out + stride*j;
|
||||||
|
int nk = width * filter_bytes;
|
||||||
int filter = *raw++;
|
int filter = *raw++;
|
||||||
|
|
||||||
if (filter > 4)
|
// check filter type
|
||||||
return stbi__err("invalid filter","Corrupt PNG");
|
if (filter > 4) {
|
||||||
|
all_ok = stbi__err("invalid filter","Corrupt PNG");
|
||||||
if (depth < 8) {
|
break;
|
||||||
if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG");
|
|
||||||
cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
|
|
||||||
filter_bytes = 1;
|
|
||||||
width = img_width_bytes;
|
|
||||||
}
|
}
|
||||||
prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
|
|
||||||
|
|
||||||
// if first row, use special filter that doesn't sample previous row
|
// if first row, use special filter that doesn't sample previous row
|
||||||
if (j == 0) filter = first_row_filter[filter];
|
if (j == 0) filter = first_row_filter[filter];
|
||||||
|
|
||||||
// handle first byte explicitly
|
// perform actual filtering
|
||||||
for (k=0; k < filter_bytes; ++k) {
|
switch (filter) {
|
||||||
switch (filter) {
|
case STBI__F_none:
|
||||||
case STBI__F_none : cur[k] = raw[k]; break;
|
memcpy(cur, raw, nk);
|
||||||
case STBI__F_sub : cur[k] = raw[k]; break;
|
break;
|
||||||
case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
|
case STBI__F_sub:
|
||||||
case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
|
memcpy(cur, raw, filter_bytes);
|
||||||
case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
|
for (k = filter_bytes; k < nk; ++k)
|
||||||
case STBI__F_avg_first : cur[k] = raw[k]; break;
|
cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
|
||||||
case STBI__F_paeth_first: cur[k] = raw[k]; break;
|
break;
|
||||||
}
|
case STBI__F_up:
|
||||||
|
for (k = 0; k < nk; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
|
||||||
|
break;
|
||||||
|
case STBI__F_avg:
|
||||||
|
for (k = 0; k < filter_bytes; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
|
||||||
|
for (k = filter_bytes; k < nk; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
|
||||||
|
break;
|
||||||
|
case STBI__F_paeth:
|
||||||
|
for (k = 0; k < filter_bytes; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0)
|
||||||
|
for (k = filter_bytes; k < nk; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes]));
|
||||||
|
break;
|
||||||
|
case STBI__F_avg_first:
|
||||||
|
memcpy(cur, raw, filter_bytes);
|
||||||
|
for (k = filter_bytes; k < nk; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth == 8) {
|
raw += nk;
|
||||||
if (img_n != out_n)
|
|
||||||
cur[img_n] = 255; // first pixel
|
|
||||||
raw += img_n;
|
|
||||||
cur += out_n;
|
|
||||||
prior += out_n;
|
|
||||||
} else if (depth == 16) {
|
|
||||||
if (img_n != out_n) {
|
|
||||||
cur[filter_bytes] = 255; // first pixel top byte
|
|
||||||
cur[filter_bytes+1] = 255; // first pixel bottom byte
|
|
||||||
}
|
|
||||||
raw += filter_bytes;
|
|
||||||
cur += output_bytes;
|
|
||||||
prior += output_bytes;
|
|
||||||
} else {
|
|
||||||
raw += 1;
|
|
||||||
cur += 1;
|
|
||||||
prior += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a little gross, so that we don't switch per-pixel or per-component
|
// expand decoded bits in cur to dest, also adding an extra alpha channel if desired
|
||||||
if (depth < 8 || img_n == out_n) {
|
if (depth < 8) {
|
||||||
int nk = (width - 1)*filter_bytes;
|
|
||||||
#define STBI__CASE(f) \
|
|
||||||
case f: \
|
|
||||||
for (k=0; k < nk; ++k)
|
|
||||||
switch (filter) {
|
|
||||||
// "none" filter turns into a memcpy here; make that explicit.
|
|
||||||
case STBI__F_none: memcpy(cur, raw, nk); break;
|
|
||||||
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
|
|
||||||
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
|
|
||||||
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
|
|
||||||
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
|
|
||||||
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
|
|
||||||
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
|
|
||||||
}
|
|
||||||
#undef STBI__CASE
|
|
||||||
raw += nk;
|
|
||||||
} else {
|
|
||||||
STBI_ASSERT(img_n+1 == out_n);
|
|
||||||
#define STBI__CASE(f) \
|
|
||||||
case f: \
|
|
||||||
for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
|
|
||||||
for (k=0; k < filter_bytes; ++k)
|
|
||||||
switch (filter) {
|
|
||||||
STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break;
|
|
||||||
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
|
|
||||||
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
|
|
||||||
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
|
|
||||||
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
|
|
||||||
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
|
|
||||||
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
|
|
||||||
}
|
|
||||||
#undef STBI__CASE
|
|
||||||
|
|
||||||
// the loop above sets the high byte of the pixels' alpha, but for
|
|
||||||
// 16 bit png files we also need the low byte set. we'll do that here.
|
|
||||||
if (depth == 16) {
|
|
||||||
cur = a->out + stride*j; // start at the beginning of the row again
|
|
||||||
for (i=0; i < x; ++i,cur+=output_bytes) {
|
|
||||||
cur[filter_bytes+1] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we make a separate pass to expand bits to pixels; for performance,
|
|
||||||
// this could run two scanlines behind the above code, so it won't
|
|
||||||
// intefere with filtering but will still be in the cache.
|
|
||||||
if (depth < 8) {
|
|
||||||
for (j=0; j < y; ++j) {
|
|
||||||
stbi_uc *cur = a->out + stride*j;
|
|
||||||
stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
|
|
||||||
// unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
|
|
||||||
// png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
|
|
||||||
stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
|
stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
|
||||||
|
stbi_uc *in = cur;
|
||||||
|
stbi_uc *out = dest;
|
||||||
|
stbi_uc inb = 0;
|
||||||
|
stbi__uint32 nsmp = x*img_n;
|
||||||
|
|
||||||
// note that the final byte might overshoot and write more data than desired.
|
// expand bits to bytes first
|
||||||
// we can allocate enough data that this never writes out of memory, but it
|
|
||||||
// could also overwrite the next scanline. can it overwrite non-empty data
|
|
||||||
// on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
|
|
||||||
// so we need to explicitly clamp the final ones
|
|
||||||
|
|
||||||
if (depth == 4) {
|
if (depth == 4) {
|
||||||
for (k=x*img_n; k >= 2; k-=2, ++in) {
|
for (i=0; i < nsmp; ++i) {
|
||||||
*cur++ = scale * ((*in >> 4) );
|
if ((i & 1) == 0) inb = *in++;
|
||||||
*cur++ = scale * ((*in ) & 0x0f);
|
*out++ = scale * (inb >> 4);
|
||||||
|
inb <<= 4;
|
||||||
}
|
}
|
||||||
if (k > 0) *cur++ = scale * ((*in >> 4) );
|
|
||||||
} else if (depth == 2) {
|
} else if (depth == 2) {
|
||||||
for (k=x*img_n; k >= 4; k-=4, ++in) {
|
for (i=0; i < nsmp; ++i) {
|
||||||
*cur++ = scale * ((*in >> 6) );
|
if ((i & 3) == 0) inb = *in++;
|
||||||
*cur++ = scale * ((*in >> 4) & 0x03);
|
*out++ = scale * (inb >> 6);
|
||||||
*cur++ = scale * ((*in >> 2) & 0x03);
|
inb <<= 2;
|
||||||
*cur++ = scale * ((*in ) & 0x03);
|
|
||||||
}
|
}
|
||||||
if (k > 0) *cur++ = scale * ((*in >> 6) );
|
} else {
|
||||||
if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
|
STBI_ASSERT(depth == 1);
|
||||||
if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
|
for (i=0; i < nsmp; ++i) {
|
||||||
} else if (depth == 1) {
|
if ((i & 7) == 0) inb = *in++;
|
||||||
for (k=x*img_n; k >= 8; k-=8, ++in) {
|
*out++ = scale * (inb >> 7);
|
||||||
*cur++ = scale * ((*in >> 7) );
|
inb <<= 1;
|
||||||
*cur++ = scale * ((*in >> 6) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 5) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 4) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 3) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 2) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 1) & 0x01);
|
|
||||||
*cur++ = scale * ((*in ) & 0x01);
|
|
||||||
}
|
}
|
||||||
if (k > 0) *cur++ = scale * ((*in >> 7) );
|
|
||||||
if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
|
|
||||||
if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
|
|
||||||
if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
|
|
||||||
if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
|
|
||||||
if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
|
|
||||||
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
|
|
||||||
}
|
}
|
||||||
if (img_n != out_n) {
|
|
||||||
int q;
|
// insert alpha=255 values if desired
|
||||||
// insert alpha = 255
|
if (img_n != out_n)
|
||||||
cur = a->out + stride*j;
|
stbi__create_png_alpha_expand8(dest, dest, x, img_n);
|
||||||
|
} else if (depth == 8) {
|
||||||
|
if (img_n == out_n)
|
||||||
|
memcpy(dest, cur, x*img_n);
|
||||||
|
else
|
||||||
|
stbi__create_png_alpha_expand8(dest, cur, x, img_n);
|
||||||
|
} else if (depth == 16) {
|
||||||
|
// convert the image data from big-endian to platform-native
|
||||||
|
stbi__uint16 *dest16 = (stbi__uint16*)dest;
|
||||||
|
stbi__uint32 nsmp = x*img_n;
|
||||||
|
|
||||||
|
if (img_n == out_n) {
|
||||||
|
for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
|
||||||
|
*dest16 = (cur[0] << 8) | cur[1];
|
||||||
|
} else {
|
||||||
|
STBI_ASSERT(img_n+1 == out_n);
|
||||||
if (img_n == 1) {
|
if (img_n == 1) {
|
||||||
for (q=x-1; q >= 0; --q) {
|
for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
|
||||||
cur[q*2+1] = 255;
|
dest16[0] = (cur[0] << 8) | cur[1];
|
||||||
cur[q*2+0] = cur[q];
|
dest16[1] = 0xffff;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
STBI_ASSERT(img_n == 3);
|
STBI_ASSERT(img_n == 3);
|
||||||
for (q=x-1; q >= 0; --q) {
|
for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
|
||||||
cur[q*4+3] = 255;
|
dest16[0] = (cur[0] << 8) | cur[1];
|
||||||
cur[q*4+2] = cur[q*3+2];
|
dest16[1] = (cur[2] << 8) | cur[3];
|
||||||
cur[q*4+1] = cur[q*3+1];
|
dest16[2] = (cur[4] << 8) | cur[5];
|
||||||
cur[q*4+0] = cur[q*3+0];
|
dest16[3] = 0xffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (depth == 16) {
|
|
||||||
// force the image data from big-endian to platform-native.
|
|
||||||
// this is done in a separate pass due to the decoding relying
|
|
||||||
// on the data being untouched, but could probably be done
|
|
||||||
// per-line during decode if care is taken.
|
|
||||||
stbi_uc *cur = a->out;
|
|
||||||
stbi__uint16 *cur16 = (stbi__uint16*)cur;
|
|
||||||
|
|
||||||
for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
|
|
||||||
*cur16 = (cur[0] << 8) | cur[1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STBI_FREE(filter_buf);
|
||||||
|
if (!all_ok) return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4955,7 +5009,7 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
|
||||||
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
|
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
|
||||||
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
|
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
|
||||||
|
|
||||||
STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
|
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
|
||||||
{
|
{
|
||||||
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
|
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
|
||||||
stbi__unpremultiply_on_load_set = 1;
|
stbi__unpremultiply_on_load_set = 1;
|
||||||
|
@ -5064,14 +5118,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
||||||
if (!pal_img_n) {
|
if (!pal_img_n) {
|
||||||
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
|
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
|
||||||
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
|
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
|
||||||
if (scan == STBI__SCAN_header) return 1;
|
|
||||||
} else {
|
} else {
|
||||||
// if paletted, then pal_n is our final components, and
|
// if paletted, then pal_n is our final components, and
|
||||||
// img_n is # components to decompress/filter.
|
// img_n is # components to decompress/filter.
|
||||||
s->img_n = 1;
|
s->img_n = 1;
|
||||||
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
|
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
|
||||||
// if SCAN_header, have to scan to see if we have a tRNS
|
|
||||||
}
|
}
|
||||||
|
// even with SCAN_header, have to scan to see if we have a tRNS
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5103,6 +5156,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
||||||
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
|
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
|
||||||
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
|
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
|
||||||
has_trans = 1;
|
has_trans = 1;
|
||||||
|
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
|
||||||
|
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
|
||||||
if (z->depth == 16) {
|
if (z->depth == 16) {
|
||||||
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
||||||
} else {
|
} else {
|
||||||
|
@ -5115,7 +5170,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
||||||
case STBI__PNG_TYPE('I','D','A','T'): {
|
case STBI__PNG_TYPE('I','D','A','T'): {
|
||||||
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
|
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
|
||||||
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
|
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
|
||||||
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
|
if (scan == STBI__SCAN_header) {
|
||||||
|
// header scan definitely stops at first IDAT
|
||||||
|
if (pal_img_n)
|
||||||
|
s->img_n = pal_img_n;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
|
||||||
if ((int)(ioff + c.length) < (int)ioff) return 0;
|
if ((int)(ioff + c.length) < (int)ioff) return 0;
|
||||||
if (ioff + c.length > idata_limit) {
|
if (ioff + c.length > idata_limit) {
|
||||||
stbi__uint32 idata_limit_old = idata_limit;
|
stbi__uint32 idata_limit_old = idata_limit;
|
||||||
|
@ -5498,8 +5559,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||||
psize = (info.offset - info.extra_read - info.hsz) >> 2;
|
psize = (info.offset - info.extra_read - info.hsz) >> 2;
|
||||||
}
|
}
|
||||||
if (psize == 0) {
|
if (psize == 0) {
|
||||||
if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) {
|
// accept some number of extra bytes after the header, but if the offset points either to before
|
||||||
return stbi__errpuc("bad offset", "Corrupt BMP");
|
// the header ends or implies a large amount of extra data, reject the file as malformed
|
||||||
|
int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);
|
||||||
|
int header_limit = 1024; // max we actually read is below 256 bytes currently.
|
||||||
|
int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.
|
||||||
|
if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {
|
||||||
|
return stbi__errpuc("bad header", "Corrupt BMP");
|
||||||
|
}
|
||||||
|
// we established that bytes_read_so_far is positive and sensible.
|
||||||
|
// the first half of this test rejects offsets that are either too small positives, or
|
||||||
|
// negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn
|
||||||
|
// ensures the number computed in the second half of the test can't overflow.
|
||||||
|
if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {
|
||||||
|
return stbi__errpuc("bad offset", "Corrupt BMP");
|
||||||
|
} else {
|
||||||
|
stbi__skip(s, info.offset - bytes_read_so_far);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7187,12 +7262,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
|
||||||
// Run
|
// Run
|
||||||
value = stbi__get8(s);
|
value = stbi__get8(s);
|
||||||
count -= 128;
|
count -= 128;
|
||||||
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
||||||
for (z = 0; z < count; ++z)
|
for (z = 0; z < count; ++z)
|
||||||
scanline[i++ * 4 + k] = value;
|
scanline[i++ * 4 + k] = value;
|
||||||
} else {
|
} else {
|
||||||
// Dump
|
// Dump
|
||||||
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
||||||
for (z = 0; z < count; ++z)
|
for (z = 0; z < count; ++z)
|
||||||
scanline[i++ * 4 + k] = stbi__get8(s);
|
scanline[i++ * 4 + k] = stbi__get8(s);
|
||||||
}
|
}
|
||||||
|
@ -7446,10 +7521,17 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||||
|
|
||||||
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
|
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
|
||||||
if (!out) return stbi__errpuc("outofmem", "Out of memory");
|
if (!out) return stbi__errpuc("outofmem", "Out of memory");
|
||||||
stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8));
|
if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
|
||||||
|
STBI_FREE(out);
|
||||||
|
return stbi__errpuc("bad PNM", "PNM file truncated");
|
||||||
|
}
|
||||||
|
|
||||||
if (req_comp && req_comp != s->img_n) {
|
if (req_comp && req_comp != s->img_n) {
|
||||||
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
|
if (ri->bits_per_channel == 16) {
|
||||||
|
out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y);
|
||||||
|
} else {
|
||||||
|
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
|
||||||
|
}
|
||||||
if (out == NULL) return out; // stbi__convert_format frees input on failure
|
if (out == NULL) return out; // stbi__convert_format frees input on failure
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
|
@ -7486,6 +7568,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c)
|
||||||
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
|
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
|
||||||
value = value*10 + (*c - '0');
|
value = value*10 + (*c - '0');
|
||||||
*c = (char) stbi__get8(s);
|
*c = (char) stbi__get8(s);
|
||||||
|
if((value > 214748364) || (value == 214748364 && *c > '7'))
|
||||||
|
return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int");
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
@ -7516,9 +7600,13 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
|
||||||
stbi__pnm_skip_whitespace(s, &c);
|
stbi__pnm_skip_whitespace(s, &c);
|
||||||
|
|
||||||
*x = stbi__pnm_getinteger(s, &c); // read width
|
*x = stbi__pnm_getinteger(s, &c); // read width
|
||||||
|
if(*x == 0)
|
||||||
|
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
|
||||||
stbi__pnm_skip_whitespace(s, &c);
|
stbi__pnm_skip_whitespace(s, &c);
|
||||||
|
|
||||||
*y = stbi__pnm_getinteger(s, &c); // read height
|
*y = stbi__pnm_getinteger(s, &c); // read height
|
||||||
|
if (*y == 0)
|
||||||
|
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
|
||||||
stbi__pnm_skip_whitespace(s, &c);
|
stbi__pnm_skip_whitespace(s, &c);
|
||||||
|
|
||||||
maxv = stbi__pnm_getinteger(s, &c); // read max value
|
maxv = stbi__pnm_getinteger(s, &c); // read max value
|
||||||
|
|
1724
Keishiki/include/ext/stb_image_write.h
Normal file
1724
Keishiki/include/ext/stb_image_write.h
Normal file
File diff suppressed because it is too large
Load diff
BIN
Keishiki/runtime/Keishiki.png
Normal file
BIN
Keishiki/runtime/Keishiki.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
10
README.md
10
README.md
|
@ -3,9 +3,15 @@
|
||||||
# Keishiki: Real-Time Compositor-Sequencer
|
# Keishiki: Real-Time Compositor-Sequencer
|
||||||
|
|
||||||
## Libraries
|
## Libraries
|
||||||
- [SDL2](https://www.libsdl.org/)
|
|
||||||
- [FreeType](https://freetype.org/)
|
|
||||||
- [bgfx](https://github.com/bkaradzic/bgfx)
|
- [bgfx](https://github.com/bkaradzic/bgfx)
|
||||||
- [Dear ImGui](https://github.com/ocornut/imgui)
|
- [Dear ImGui](https://github.com/ocornut/imgui)
|
||||||
|
- [FreeType](https://freetype.org/)
|
||||||
- [imgui_impl_bgfx](https://gist.github.com/pr0g/aff79b71bf9804ddb03f39ca7c0c3bbb)
|
- [imgui_impl_bgfx](https://gist.github.com/pr0g/aff79b71bf9804ddb03f39ca7c0c3bbb)
|
||||||
|
- [SDL2](https://www.libsdl.org/)
|
||||||
- [SRELL](https://www.akenotsuki.com/misc/srell/en/)
|
- [SRELL](https://www.akenotsuki.com/misc/srell/en/)
|
||||||
|
|
||||||
|
## Inspired by
|
||||||
|
- [Automaton](https://github.com/0b5vr/automaton)
|
||||||
|
- [Blender](https://www.blender.org/)
|
||||||
|
- [Adobe After Effects](https://www.adobe.com/products/aftereffects)
|
||||||
|
- [Houdini](https://www.sidefx.com/products/houdini/)
|
||||||
|
|
5
TODO.md
5
TODO.md
|
@ -1,5 +1,5 @@
|
||||||
## Compositor
|
## Compositor
|
||||||
- Manage Samplers
|
- Manage Samplers -- (Resources in general)
|
||||||
- Data model for comps
|
- Data model for comps
|
||||||
- Dump and read back state, (de)serialization!!!
|
- Dump and read back state, (de)serialization!!!
|
||||||
- std::unordered_map -> std::flat_map pending compiler support
|
- std::unordered_map -> std::flat_map pending compiler support
|
||||||
|
@ -13,6 +13,5 @@
|
||||||
- Wait for SDL3!
|
- Wait for SDL3!
|
||||||
|
|
||||||
## IO
|
## IO
|
||||||
- PNG Export (perhaps go stb -> bimg)
|
|
||||||
- Video import (research opencv libavcodec etc)
|
- Video import (research opencv libavcodec etc)
|
||||||
- Dialogs pending SDL3
|
- File dialogues pending SDL3
|
||||||
|
|
Loading…
Add table
Reference in a new issue