initial commit
This commit is contained in:
commit
7e7207a87e
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
*.DNG
|
||||
*.dng
|
||||
*.jpg
|
||||
*.ARW
|
||||
*.arw
|
||||
example_*
|
||||
tedit
|
||||
asdf
|
||||
|
||||
.vscode/
|
92
Makefile
Normal file
92
Makefile
Normal file
@ -0,0 +1,92 @@
|
||||
#
|
||||
# Cross Platform Makefile
|
||||
# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X
|
||||
#
|
||||
# You will need SDL2 (http://www.libsdl.org):
|
||||
# Linux:
|
||||
# apt-get install libsdl2-dev
|
||||
# Mac OS X:
|
||||
# brew install sdl2
|
||||
# MSYS2:
|
||||
# pacman -S mingw-w64-i686-SDL2
|
||||
#
|
||||
|
||||
#CXX = g++
|
||||
#CXX = clang++
|
||||
|
||||
EXE = tedit
|
||||
IMGUI_DIR = ../..
|
||||
SOURCES = main.cpp exif.cpp imgui_tex_inspect.cpp tex_inspect_opengl.cpp
|
||||
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
|
||||
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl2.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
|
||||
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
|
||||
UNAME_S := $(shell uname -s)
|
||||
LINUX_GL_LIBS = -lGLEW -lGL -ldl
|
||||
|
||||
CXXFLAGS = -std=c++17 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
|
||||
CXXFLAGS += -g -Wall -Wformat -O3
|
||||
LIBS = -lraw -ljpeg -lpng -ltiff -lz -lm -DIMGUI -DIMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## OPENGL ES
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
## This assumes a GL ES library available in the system, e.g. libGLESv2.so
|
||||
# CXXFLAGS += -DIMGUI_IMPL_OPENGL_ES2
|
||||
# LINUX_GL_LIBS = -lGLESv2
|
||||
## If you're on a Raspberry Pi and want to use the legacy drivers,
|
||||
## use the following instead:
|
||||
# LINUX_GL_LIBS = -L/opt/vc/lib -lbrcmGLESv2
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## BUILD FLAGS PER PLATFORM
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
ifeq ($(UNAME_S), Linux) #LINUX
|
||||
ECHO_MESSAGE = "Linux"
|
||||
LIBS += $(LINUX_GL_LIBS) -ldl `sdl2-config --libs`
|
||||
|
||||
CXXFLAGS += `sdl2-config --cflags`
|
||||
CFLAGS = $(CXXFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S), Darwin) #APPLE
|
||||
ECHO_MESSAGE = "Mac OS X"
|
||||
LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl2-config --libs`
|
||||
LIBS += -L/usr/local/lib -L/opt/local/lib
|
||||
|
||||
CXXFLAGS += `sdl2-config --cflags`
|
||||
CXXFLAGS += -I/usr/local/include -I/opt/local/include
|
||||
CFLAGS = $(CXXFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
ECHO_MESSAGE = "MinGW"
|
||||
LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2`
|
||||
|
||||
CXXFLAGS += `pkg-config --cflags sdl2`
|
||||
CFLAGS = $(CXXFLAGS)
|
||||
endif
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## BUILD RULES
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
%.o:%.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/%.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/backends/%.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
all: $(EXE)
|
||||
@echo Build complete for $(ECHO_MESSAGE)
|
||||
|
||||
$(EXE): $(OBJS)
|
||||
$(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(EXE) $(OBJS)
|
95
Makefile.emscripten
Normal file
95
Makefile.emscripten
Normal file
@ -0,0 +1,95 @@
|
||||
#
|
||||
# Makefile to use with SDL+emscripten
|
||||
# See https://emscripten.org/docs/getting_started/downloads.html
|
||||
# for installation instructions.
|
||||
#
|
||||
# This Makefile assumes you have loaded emscripten's environment.
|
||||
# (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead)
|
||||
#
|
||||
# Running `make -f Makefile.emscripten` will produce three files:
|
||||
# - web/index.html
|
||||
# - web/index.js
|
||||
# - web/index.wasm
|
||||
#
|
||||
# All three are needed to run the demo.
|
||||
|
||||
CC = emcc
|
||||
CXX = em++
|
||||
WEB_DIR = web
|
||||
EXE = $(WEB_DIR)/index.html
|
||||
IMGUI_DIR = ../..
|
||||
SOURCES = main.cpp exif.cpp imgui_tex_inspect.cpp tex_inspect_opengl.cpp
|
||||
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
|
||||
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl2.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
|
||||
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
|
||||
UNAME_S := $(shell uname -s)
|
||||
CPPFLAGS =
|
||||
LDFLAGS = -lraw -ljpeg -lpng -ltiff -lz -lm -DIMGUI -DIMGUI_DEFINE_MATH_OPERATORS
|
||||
EMS =
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## EMSCRIPTEN OPTIONS
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
# ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only)
|
||||
EMS += -s USE_SDL=2
|
||||
EMS += -s DISABLE_EXCEPTION_CATCHING=1
|
||||
LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1
|
||||
|
||||
# Build as single file (binary text encoded in .html file)
|
||||
#LDFLAGS += -sSINGLE_FILE
|
||||
|
||||
# Uncomment next line to fix possible rendering bugs with Emscripten version older then 1.39.0 (https://github.com/ocornut/imgui/issues/2877)
|
||||
#EMS += -s BINARYEN_TRAP_MODE=clamp
|
||||
#EMS += -s SAFE_HEAP=1 ## Adds overhead
|
||||
|
||||
# Emscripten allows preloading a file or folder to be accessible at runtime.
|
||||
# The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts"
|
||||
# See documentation for more details: https://emscripten.org/docs/porting/files/packaging_files.html
|
||||
# (Default value is 0. Set to 1 to enable file-system and include the misc/fonts/ folder as part of the build.)
|
||||
USE_FILE_SYSTEM ?= 0
|
||||
ifeq ($(USE_FILE_SYSTEM), 0)
|
||||
LDFLAGS += -s NO_FILESYSTEM=1
|
||||
CPPFLAGS += -DIMGUI_DISABLE_FILE_FUNCTIONS
|
||||
endif
|
||||
ifeq ($(USE_FILE_SYSTEM), 1)
|
||||
LDFLAGS += --no-heap-copy --preload-file ../../misc/fonts@/fonts
|
||||
endif
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## FINAL BUILD FLAGS
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
|
||||
#CPPFLAGS += -g
|
||||
CPPFLAGS += -Wall -Wformat -Os $(EMS)
|
||||
LDFLAGS += --shell-file ../libs/emscripten/shell_minimal.html
|
||||
LDFLAGS += $(EMS)
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## BUILD RULES
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
%.o:%.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/%.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/backends/%.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
all: $(EXE)
|
||||
@echo Build complete for $(EXE)
|
||||
|
||||
$(WEB_DIR):
|
||||
mkdir $@
|
||||
|
||||
serve: all
|
||||
python3 -m http.server -d $(WEB_DIR)
|
||||
|
||||
$(EXE): $(OBJS) $(WEB_DIR)
|
||||
$(CXX) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJS) $(WEB_DIR)
|
1759
app_image.h
Normal file
1759
app_image.h
Normal file
File diff suppressed because it is too large
Load Diff
918
exif.cpp
Normal file
918
exif.cpp
Normal file
@ -0,0 +1,918 @@
|
||||
/**************************************************************************
|
||||
exif.cpp -- A simple ISO C++ library to parse basic EXIF
|
||||
information from a JPEG file.
|
||||
|
||||
Copyright (c) 2010-2015 Mayank Lahiri
|
||||
mlahiri@gmail.com
|
||||
All rights reserved (BSD License).
|
||||
|
||||
See exif.h for version history.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
-- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
-- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "exif.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Rational {
|
||||
uint32_t numerator, denominator;
|
||||
operator double() const {
|
||||
if (denominator < 1e-20) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<double>(numerator) / static_cast<double>(denominator);
|
||||
}
|
||||
};
|
||||
|
||||
// IF Entry
|
||||
class IFEntry {
|
||||
public:
|
||||
using byte_vector = std::vector<uint8_t>;
|
||||
using ascii_vector = std::string;
|
||||
using short_vector = std::vector<uint16_t>;
|
||||
using long_vector = std::vector<uint32_t>;
|
||||
using rational_vector = std::vector<Rational>;
|
||||
|
||||
IFEntry()
|
||||
: tag_(0xFF), format_(0xFF), data_(0), length_(0), val_byte_(nullptr) {}
|
||||
IFEntry(const IFEntry &) = delete;
|
||||
IFEntry &operator=(const IFEntry &) = delete;
|
||||
IFEntry(IFEntry &&other)
|
||||
: tag_(other.tag_),
|
||||
format_(other.format_),
|
||||
data_(other.data_),
|
||||
length_(other.length_),
|
||||
val_byte_(other.val_byte_) {
|
||||
other.tag_ = 0xFF;
|
||||
other.format_ = 0xFF;
|
||||
other.data_ = 0;
|
||||
other.length_ = 0;
|
||||
other.val_byte_ = nullptr;
|
||||
}
|
||||
~IFEntry() { delete_union(); }
|
||||
unsigned short tag() const { return tag_; }
|
||||
void tag(unsigned short tag) { tag_ = tag; }
|
||||
unsigned short format() const { return format_; }
|
||||
bool format(unsigned short format) {
|
||||
switch (format) {
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x07:
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
case 0xff:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
delete_union();
|
||||
format_ = format;
|
||||
new_union();
|
||||
return true;
|
||||
}
|
||||
unsigned data() const { return data_; }
|
||||
void data(unsigned data) { data_ = data; }
|
||||
unsigned length() const { return length_; }
|
||||
void length(unsigned length) { length_ = length; }
|
||||
|
||||
// functions to access the data
|
||||
//
|
||||
// !! it's CALLER responsibility to check that format !!
|
||||
// !! is correct before accessing it's field !!
|
||||
//
|
||||
// - getters are use here to allow future addition
|
||||
// of checks if format is correct
|
||||
byte_vector &val_byte() { return *val_byte_; }
|
||||
ascii_vector &val_string() { return *val_string_; }
|
||||
short_vector &val_short() { return *val_short_; }
|
||||
long_vector &val_long() { return *val_long_; }
|
||||
rational_vector &val_rational() { return *val_rational_; }
|
||||
|
||||
private:
|
||||
// Raw fields
|
||||
unsigned short tag_;
|
||||
unsigned short format_;
|
||||
unsigned data_;
|
||||
unsigned length_;
|
||||
|
||||
// Parsed fields
|
||||
union {
|
||||
byte_vector *val_byte_;
|
||||
ascii_vector *val_string_;
|
||||
short_vector *val_short_;
|
||||
long_vector *val_long_;
|
||||
rational_vector *val_rational_;
|
||||
};
|
||||
|
||||
void delete_union() {
|
||||
switch (format_) {
|
||||
case 0x1:
|
||||
delete val_byte_;
|
||||
val_byte_ = nullptr;
|
||||
break;
|
||||
case 0x2:
|
||||
delete val_string_;
|
||||
val_string_ = nullptr;
|
||||
break;
|
||||
case 0x3:
|
||||
delete val_short_;
|
||||
val_short_ = nullptr;
|
||||
break;
|
||||
case 0x4:
|
||||
delete val_long_;
|
||||
val_long_ = nullptr;
|
||||
break;
|
||||
case 0x5:
|
||||
delete val_rational_;
|
||||
val_rational_ = nullptr;
|
||||
break;
|
||||
case 0xff:
|
||||
break;
|
||||
default:
|
||||
// should not get here
|
||||
// should I throw an exception or ...?
|
||||
break;
|
||||
}
|
||||
}
|
||||
void new_union() {
|
||||
switch (format_) {
|
||||
case 0x1:
|
||||
val_byte_ = new byte_vector();
|
||||
break;
|
||||
case 0x2:
|
||||
val_string_ = new ascii_vector();
|
||||
break;
|
||||
case 0x3:
|
||||
val_short_ = new short_vector();
|
||||
break;
|
||||
case 0x4:
|
||||
val_long_ = new long_vector();
|
||||
break;
|
||||
case 0x5:
|
||||
val_rational_ = new rational_vector();
|
||||
break;
|
||||
case 0xff:
|
||||
break;
|
||||
default:
|
||||
// should not get here
|
||||
// should I throw an exception or ...?
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Helper functions
|
||||
template <typename T, bool alignIntel>
|
||||
T parse(const unsigned char *buf);
|
||||
|
||||
template <>
|
||||
uint8_t parse<uint8_t, false>(const unsigned char *buf) {
|
||||
return *buf;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint8_t parse<uint8_t, true>(const unsigned char *buf) {
|
||||
return *buf;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint16_t parse<uint16_t, false>(const unsigned char *buf) {
|
||||
return (static_cast<uint16_t>(buf[0]) << 8) | buf[1];
|
||||
}
|
||||
|
||||
template <>
|
||||
uint16_t parse<uint16_t, true>(const unsigned char *buf) {
|
||||
return (static_cast<uint16_t>(buf[1]) << 8) | buf[0];
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t parse<uint32_t, false>(const unsigned char *buf) {
|
||||
return (static_cast<uint32_t>(buf[0]) << 24) |
|
||||
(static_cast<uint32_t>(buf[1]) << 16) |
|
||||
(static_cast<uint32_t>(buf[2]) << 8) | buf[3];
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t parse<uint32_t, true>(const unsigned char *buf) {
|
||||
return (static_cast<uint32_t>(buf[3]) << 24) |
|
||||
(static_cast<uint32_t>(buf[2]) << 16) |
|
||||
(static_cast<uint32_t>(buf[1]) << 8) | buf[0];
|
||||
}
|
||||
|
||||
template <>
|
||||
Rational parse<Rational, true>(const unsigned char *buf) {
|
||||
Rational r;
|
||||
r.numerator = parse<uint32_t, true>(buf);
|
||||
r.denominator = parse<uint32_t, true>(buf + 4);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <>
|
||||
Rational parse<Rational, false>(const unsigned char *buf) {
|
||||
Rational r;
|
||||
r.numerator = parse<uint32_t, false>(buf);
|
||||
r.denominator = parse<uint32_t, false>(buf + 4);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to read entry.length() values for this entry.
|
||||
*
|
||||
* Returns:
|
||||
* true - entry.length() values were read
|
||||
* false - something went wrong, vec's content was not touched
|
||||
*/
|
||||
template <typename T, bool alignIntel, typename C>
|
||||
bool extract_values(C &container, const unsigned char *buf, const unsigned base,
|
||||
const unsigned len, const IFEntry &entry) {
|
||||
const unsigned char *data;
|
||||
uint32_t reversed_data;
|
||||
// if data fits into 4 bytes, they are stored directly in
|
||||
// the data field in IFEntry
|
||||
if (sizeof(T) * entry.length() <= 4) {
|
||||
if (alignIntel) {
|
||||
reversed_data = entry.data();
|
||||
} else {
|
||||
reversed_data = entry.data();
|
||||
// this reversing works, but is ugly
|
||||
unsigned char *rdata = reinterpret_cast<unsigned char *>(&reversed_data);
|
||||
unsigned char tmp;
|
||||
tmp = rdata[0];
|
||||
rdata[0] = rdata[3];
|
||||
rdata[3] = tmp;
|
||||
tmp = rdata[1];
|
||||
rdata[1] = rdata[2];
|
||||
rdata[2] = tmp;
|
||||
}
|
||||
data = reinterpret_cast<const unsigned char *>(&(reversed_data));
|
||||
} else {
|
||||
data = buf + base + entry.data();
|
||||
if (data + sizeof(T) * entry.length() > buf + len) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
container.resize(entry.length());
|
||||
for (size_t i = 0; i < entry.length(); ++i) {
|
||||
container[i] = parse<T, alignIntel>(data + sizeof(T) * i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <bool alignIntel>
|
||||
void parseIFEntryHeader(const unsigned char *buf, unsigned short &tag,
|
||||
unsigned short &format, unsigned &length,
|
||||
unsigned &data) {
|
||||
// Each directory entry is composed of:
|
||||
// 2 bytes: tag number (data field)
|
||||
// 2 bytes: data format
|
||||
// 4 bytes: number of components
|
||||
// 4 bytes: data value or offset to data value
|
||||
tag = parse<uint16_t, alignIntel>(buf);
|
||||
format = parse<uint16_t, alignIntel>(buf + 2);
|
||||
length = parse<uint32_t, alignIntel>(buf + 4);
|
||||
data = parse<uint32_t, alignIntel>(buf + 8);
|
||||
}
|
||||
|
||||
template <bool alignIntel>
|
||||
void parseIFEntryHeader(const unsigned char *buf, IFEntry &result) {
|
||||
unsigned short tag;
|
||||
unsigned short format;
|
||||
unsigned length;
|
||||
unsigned data;
|
||||
|
||||
parseIFEntryHeader<alignIntel>(buf, tag, format, length, data);
|
||||
|
||||
result.tag(tag);
|
||||
result.format(format);
|
||||
result.length(length);
|
||||
result.data(data);
|
||||
}
|
||||
|
||||
template <bool alignIntel>
|
||||
IFEntry parseIFEntry_temp(const unsigned char *buf, const unsigned offs,
|
||||
const unsigned base, const unsigned len) {
|
||||
IFEntry result;
|
||||
|
||||
// check if there even is enough data for IFEntry in the buffer
|
||||
if (buf + offs + 12 > buf + len) {
|
||||
result.tag(0xFF);
|
||||
return result;
|
||||
}
|
||||
|
||||
parseIFEntryHeader<alignIntel>(buf + offs, result);
|
||||
|
||||
// Parse value in specified format
|
||||
switch (result.format()) {
|
||||
case 1:
|
||||
if (!extract_values<uint8_t, alignIntel>(result.val_byte(), buf, base,
|
||||
len, result)) {
|
||||
result.tag(0xFF);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// string is basically sequence of uint8_t (well, according to EXIF even
|
||||
// uint7_t, but
|
||||
// we don't have that), so just read it as bytes
|
||||
if (!extract_values<uint8_t, alignIntel>(result.val_string(), buf, base,
|
||||
len, result)) {
|
||||
result.tag(0xFF);
|
||||
}
|
||||
// and cut zero byte at the end, since we don't want that in the
|
||||
// std::string
|
||||
if (result.val_string()[result.val_string().length() - 1] == '\0') {
|
||||
result.val_string().resize(result.val_string().length() - 1);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!extract_values<uint16_t, alignIntel>(result.val_short(), buf, base,
|
||||
len, result)) {
|
||||
result.tag(0xFF);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (!extract_values<uint32_t, alignIntel>(result.val_long(), buf, base,
|
||||
len, result)) {
|
||||
result.tag(0xFF);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (!extract_values<Rational, alignIntel>(result.val_rational(), buf,
|
||||
base, len, result)) {
|
||||
result.tag(0xFF);
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
case 9:
|
||||
case 10:
|
||||
break;
|
||||
default:
|
||||
result.tag(0xFF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// helper functions for convinience
|
||||
template <typename T>
|
||||
T parse_value(const unsigned char *buf, bool alignIntel) {
|
||||
if (alignIntel) {
|
||||
return parse<T, true>(buf);
|
||||
} else {
|
||||
return parse<T, false>(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void parseIFEntryHeader(const unsigned char *buf, bool alignIntel,
|
||||
unsigned short &tag, unsigned short &format,
|
||||
unsigned &length, unsigned &data) {
|
||||
if (alignIntel) {
|
||||
parseIFEntryHeader<true>(buf, tag, format, length, data);
|
||||
} else {
|
||||
parseIFEntryHeader<false>(buf, tag, format, length, data);
|
||||
}
|
||||
}
|
||||
|
||||
IFEntry parseIFEntry(const unsigned char *buf, const unsigned offs,
|
||||
const bool alignIntel, const unsigned base,
|
||||
const unsigned len) {
|
||||
if (alignIntel) {
|
||||
return parseIFEntry_temp<true>(buf, offs, base, len);
|
||||
} else {
|
||||
return parseIFEntry_temp<false>(buf, offs, base, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Locates the EXIF segment and parses it using parseFromEXIFSegment
|
||||
//
|
||||
int easyexif::EXIFInfo::parseFrom(const unsigned char *buf, unsigned len) {
|
||||
// Sanity check: all JPEG files start with 0xFFD8.
|
||||
if (!buf || len < 4) return PARSE_EXIF_ERROR_NO_JPEG;
|
||||
if (buf[0] != 0xFF || buf[1] != 0xD8) return PARSE_EXIF_ERROR_NO_JPEG;
|
||||
|
||||
// Sanity check: some cameras pad the JPEG image with some bytes at the end.
|
||||
// Normally, we should be able to find the JPEG end marker 0xFFD9 at the end
|
||||
// of the image buffer, but not always. As long as there are some bytes
|
||||
// except 0xD9 at the end of the image buffer, keep decrementing len until
|
||||
// an 0xFFD9 is found. If JPEG end marker 0xFFD9 is not found,
|
||||
// then we can be reasonably sure that the buffer is not a JPEG.
|
||||
while (len > 2) {
|
||||
if (buf[len - 1] == 0xD9 && buf[len - 2] == 0xFF)
|
||||
break;
|
||||
len--;
|
||||
}
|
||||
if (len <= 2)
|
||||
return PARSE_EXIF_ERROR_NO_JPEG;
|
||||
|
||||
clear();
|
||||
|
||||
// Scan for EXIF header (bytes 0xFF 0xE1) and do a sanity check by
|
||||
// looking for bytes "Exif\0\0". The marker length data is in Motorola
|
||||
// byte order, which results in the 'false' parameter to parse16().
|
||||
// The marker has to contain at least the TIFF header, otherwise the
|
||||
// EXIF data is corrupt. So the minimum length specified here has to be:
|
||||
// 2 bytes: section size
|
||||
// 6 bytes: "Exif\0\0" string
|
||||
// 2 bytes: TIFF header (either "II" or "MM" string)
|
||||
// 2 bytes: TIFF magic (short 0x2a00 in Motorola byte order)
|
||||
// 4 bytes: Offset to first IFD
|
||||
// =========
|
||||
// 16 bytes
|
||||
unsigned offs = 0; // current offset into buffer
|
||||
for (offs = 0; offs < len - 1; offs++)
|
||||
if (buf[offs] == 0xFF && buf[offs + 1] == 0xE1) break;
|
||||
if (offs + 4 > len) return PARSE_EXIF_ERROR_NO_EXIF;
|
||||
offs += 2;
|
||||
unsigned short section_length = parse_value<uint16_t>(buf + offs, false);
|
||||
if (offs + section_length > len || section_length < 16)
|
||||
return PARSE_EXIF_ERROR_CORRUPT;
|
||||
offs += 2;
|
||||
|
||||
return parseFromEXIFSegment(buf + offs, len - offs);
|
||||
}
|
||||
|
||||
int easyexif::EXIFInfo::parseFrom(const string &data) {
|
||||
return parseFrom(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), static_cast<unsigned>(data.length()));
|
||||
}
|
||||
|
||||
//
|
||||
// Main parsing function for an EXIF segment.
|
||||
//
|
||||
// PARAM: 'buf' start of the EXIF TIFF, which must be the bytes "Exif\0\0".
|
||||
// PARAM: 'len' length of buffer
|
||||
//
|
||||
int easyexif::EXIFInfo::parseFromEXIFSegment(const unsigned char *buf,
|
||||
unsigned len) {
|
||||
bool alignIntel = true; // byte alignment (defined in EXIF header)
|
||||
unsigned offs = 0; // current offset into buffer
|
||||
if (!buf || len < 6) return PARSE_EXIF_ERROR_NO_EXIF;
|
||||
|
||||
if (!std::equal(buf, buf + 6, "Exif\0\0")) return PARSE_EXIF_ERROR_NO_EXIF;
|
||||
offs += 6;
|
||||
|
||||
// Now parsing the TIFF header. The first two bytes are either "II" or
|
||||
// "MM" for Intel or Motorola byte alignment. Sanity check by parsing
|
||||
// the unsigned short that follows, making sure it equals 0x2a. The
|
||||
// last 4 bytes are an offset into the first IFD, which are added to
|
||||
// the global offset counter. For this block, we expect the following
|
||||
// minimum size:
|
||||
// 2 bytes: 'II' or 'MM'
|
||||
// 2 bytes: 0x002a
|
||||
// 4 bytes: offset to first IDF
|
||||
// -----------------------------
|
||||
// 8 bytes
|
||||
if (offs + 8 > len) return PARSE_EXIF_ERROR_CORRUPT;
|
||||
unsigned tiff_header_start = offs;
|
||||
if (buf[offs] == 'I' && buf[offs + 1] == 'I')
|
||||
alignIntel = true;
|
||||
else {
|
||||
if (buf[offs] == 'M' && buf[offs + 1] == 'M')
|
||||
alignIntel = false;
|
||||
else
|
||||
return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN;
|
||||
}
|
||||
this->ByteAlign = alignIntel;
|
||||
offs += 2;
|
||||
if (0x2a != parse_value<uint16_t>(buf + offs, alignIntel))
|
||||
return PARSE_EXIF_ERROR_CORRUPT;
|
||||
offs += 2;
|
||||
unsigned first_ifd_offset = parse_value<uint32_t>(buf + offs, alignIntel);
|
||||
offs += first_ifd_offset - 4;
|
||||
if (offs >= len) return PARSE_EXIF_ERROR_CORRUPT;
|
||||
|
||||
// Now parsing the first Image File Directory (IFD0, for the main image).
|
||||
// An IFD consists of a variable number of 12-byte directory entries. The
|
||||
// first two bytes of the IFD section contain the number of directory
|
||||
// entries in the section. The last 4 bytes of the IFD contain an offset
|
||||
// to the next IFD, which means this IFD must contain exactly 6 + 12 * num
|
||||
// bytes of data.
|
||||
if (offs + 2 > len) return PARSE_EXIF_ERROR_CORRUPT;
|
||||
int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
|
||||
if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
|
||||
offs += 2;
|
||||
unsigned exif_sub_ifd_offset = len;
|
||||
unsigned gps_sub_ifd_offset = len;
|
||||
while (--num_entries >= 0) {
|
||||
IFEntry result =
|
||||
parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
|
||||
offs += 12;
|
||||
switch (result.tag()) {
|
||||
case 0x102:
|
||||
// Bits per sample
|
||||
if (result.format() == 3 && result.val_short().size())
|
||||
this->BitsPerSample = result.val_short().front();
|
||||
break;
|
||||
|
||||
case 0x10E:
|
||||
// Image description
|
||||
if (result.format() == 2) this->ImageDescription = result.val_string();
|
||||
break;
|
||||
|
||||
case 0x10F:
|
||||
// Digicam make
|
||||
if (result.format() == 2) this->Make = result.val_string();
|
||||
break;
|
||||
|
||||
case 0x110:
|
||||
// Digicam model
|
||||
if (result.format() == 2) this->Model = result.val_string();
|
||||
break;
|
||||
|
||||
case 0x112:
|
||||
// Orientation of image
|
||||
if (result.format() == 3 && result.val_short().size())
|
||||
this->Orientation = result.val_short().front();
|
||||
break;
|
||||
|
||||
case 0x131:
|
||||
// Software used for image
|
||||
if (result.format() == 2) this->Software = result.val_string();
|
||||
break;
|
||||
|
||||
case 0x132:
|
||||
// EXIF/TIFF date/time of image modification
|
||||
if (result.format() == 2) this->DateTime = result.val_string();
|
||||
break;
|
||||
|
||||
case 0x8298:
|
||||
// Copyright information
|
||||
if (result.format() == 2) this->Copyright = result.val_string();
|
||||
break;
|
||||
|
||||
case 0x8825:
|
||||
// GPS IFS offset
|
||||
gps_sub_ifd_offset = tiff_header_start + result.data();
|
||||
break;
|
||||
|
||||
case 0x8769:
|
||||
// EXIF SubIFD offset
|
||||
exif_sub_ifd_offset = tiff_header_start + result.data();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Jump to the EXIF SubIFD if it exists and parse all the information
|
||||
// there. Note that it's possible that the EXIF SubIFD doesn't exist.
|
||||
// The EXIF SubIFD contains most of the interesting information that a
|
||||
// typical user might want.
|
||||
if (exif_sub_ifd_offset + 4 <= len) {
|
||||
offs = exif_sub_ifd_offset;
|
||||
int num_sub_entries = parse_value<uint16_t>(buf + offs, alignIntel);
|
||||
if (offs + 6 + 12 * num_sub_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
|
||||
offs += 2;
|
||||
while (--num_sub_entries >= 0) {
|
||||
IFEntry result =
|
||||
parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
|
||||
switch (result.tag()) {
|
||||
case 0x829a:
|
||||
// Exposure time in seconds
|
||||
if (result.format() == 5 && result.val_rational().size())
|
||||
this->ExposureTime = result.val_rational().front();
|
||||
break;
|
||||
|
||||
case 0x829d:
|
||||
// FNumber
|
||||
if (result.format() == 5 && result.val_rational().size())
|
||||
this->FNumber = result.val_rational().front();
|
||||
break;
|
||||
|
||||
case 0x8822:
|
||||
// Exposure Program
|
||||
if (result.format() == 3 && result.val_short().size())
|
||||
this->ExposureProgram = result.val_short().front();
|
||||
break;
|
||||
|
||||
case 0x8827:
|
||||
// ISO Speed Rating
|
||||
if (result.format() == 3 && result.val_short().size())
|
||||
this->ISOSpeedRatings = result.val_short().front();
|
||||
break;
|
||||
|
||||
case 0x9003:
|
||||
// Original date and time
|
||||
if (result.format() == 2)
|
||||
this->DateTimeOriginal = result.val_string();
|
||||
break;
|
||||
|
||||
case 0x9004:
|
||||
// Digitization date and time
|
||||
if (result.format() == 2)
|
||||
this->DateTimeDigitized = result.val_string();
|
||||
break;
|
||||
|
||||
case 0x9201:
|
||||
// Shutter speed value
|
||||
if (result.format() == 5 && result.val_rational().size())
|
||||
this->ShutterSpeedValue = result.val_rational().front();
|
||||
break;
|
||||
|
||||
case 0x9204:
|
||||
// Exposure bias value
|
||||
if (result.format() == 5 && result.val_rational().size())
|
||||
this->ExposureBiasValue = result.val_rational().front();
|
||||
break;
|
||||
|
||||
case 0x9206:
|
||||
// Subject distance
|
||||
if (result.format() == 5 && result.val_rational().size())
|
||||
this->SubjectDistance = result.val_rational().front();
|
||||
break;
|
||||
|
||||
case 0x9209:
|
||||
// Flash used
|
||||
if (result.format() == 3 && result.val_short().size()) {
|
||||
uint16_t data = result.val_short().front();
|
||||
|
||||
this->Flash = data & 1;
|
||||
this->FlashReturnedLight = (data & 6) >> 1;
|
||||
this->FlashMode = (data & 24) >> 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x920a:
|
||||
// Focal length
|
||||
if (result.format() == 5 && result.val_rational().size())
|
||||
this->FocalLength = result.val_rational().front();
|
||||
break;
|
||||
|
||||
case 0x9207:
|
||||
// Metering mode
|
||||
if (result.format() == 3 && result.val_short().size())
|
||||
this->MeteringMode = result.val_short().front();
|
||||
break;
|
||||
|
||||
case 0x9291:
|
||||
// Subsecond original time
|
||||
if (result.format() == 2)
|
||||
this->SubSecTimeOriginal = result.val_string();
|
||||
break;
|
||||
|
||||
case 0xa002:
|
||||
// EXIF Image width
|
||||
if (result.format() == 4 && result.val_long().size())
|
||||
this->ImageWidth = result.val_long().front();
|
||||
if (result.format() == 3 && result.val_short().size())
|
||||
this->ImageWidth = result.val_short().front();
|
||||
break;
|
||||
|
||||
case 0xa003:
|
||||
// EXIF Image height
|
||||
if (result.format() == 4 && result.val_long().size())
|
||||
this->ImageHeight = result.val_long().front();
|
||||
if (result.format() == 3 && result.val_short().size())
|
||||
this->ImageHeight = result.val_short().front();
|
||||
break;
|
||||
|
||||
case 0xa20e:
|
||||
// EXIF Focal plane X-resolution
|
||||
if (result.format() == 5) {
|
||||
this->LensInfo.FocalPlaneXResolution = result.val_rational()[0];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa20f:
|
||||
// EXIF Focal plane Y-resolution
|
||||
if (result.format() == 5) {
|
||||
this->LensInfo.FocalPlaneYResolution = result.val_rational()[0];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa210:
|
||||
// EXIF Focal plane resolution unit
|
||||
if (result.format() == 3 && result.val_short().size()) {
|
||||
this->LensInfo.FocalPlaneResolutionUnit = result.val_short().front();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa405:
|
||||
// Focal length in 35mm film
|
||||
if (result.format() == 3 && result.val_short().size())
|
||||
this->FocalLengthIn35mm = result.val_short().front();
|
||||
break;
|
||||
|
||||
case 0xa432:
|
||||
// Focal length and FStop.
|
||||
if (result.format() == 5) {
|
||||
int sz = static_cast<unsigned>(result.val_rational().size());
|
||||
if (sz)
|
||||
this->LensInfo.FocalLengthMin = result.val_rational()[0];
|
||||
if (sz > 1)
|
||||
this->LensInfo.FocalLengthMax = result.val_rational()[1];
|
||||
if (sz > 2)
|
||||
this->LensInfo.FStopMin = result.val_rational()[2];
|
||||
if (sz > 3)
|
||||
this->LensInfo.FStopMax = result.val_rational()[3];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa433:
|
||||
// Lens make.
|
||||
if (result.format() == 2) {
|
||||
this->LensInfo.Make = result.val_string();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa434:
|
||||
// Lens model.
|
||||
if (result.format() == 2) {
|
||||
this->LensInfo.Model = result.val_string();
|
||||
}
|
||||
break;
|
||||
}
|
||||
offs += 12;
|
||||
}
|
||||
}
|
||||
|
||||
// Jump to the GPS SubIFD if it exists and parse all the information
|
||||
// there. Note that it's possible that the GPS SubIFD doesn't exist.
|
||||
if (gps_sub_ifd_offset + 4 <= len) {
|
||||
offs = gps_sub_ifd_offset;
|
||||
int num_sub_entries = parse_value<uint16_t>(buf + offs, alignIntel);
|
||||
if (offs + 6 + 12 * num_sub_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
|
||||
offs += 2;
|
||||
while (--num_sub_entries >= 0) {
|
||||
unsigned short tag, format;
|
||||
unsigned length, data;
|
||||
parseIFEntryHeader(buf + offs, alignIntel, tag, format, length, data);
|
||||
switch (tag) {
|
||||
case 1:
|
||||
// GPS north or south
|
||||
this->GeoLocation.LatComponents.direction = *(buf + offs + 8);
|
||||
if (this->GeoLocation.LatComponents.direction == 0) {
|
||||
this->GeoLocation.LatComponents.direction = '?';
|
||||
}
|
||||
if ('S' == this->GeoLocation.LatComponents.direction) {
|
||||
this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// GPS latitude
|
||||
if ((format == 5 || format == 10) && length == 3) {
|
||||
this->GeoLocation.LatComponents.degrees = parse_value<Rational>(
|
||||
buf + data + tiff_header_start, alignIntel);
|
||||
this->GeoLocation.LatComponents.minutes = parse_value<Rational>(
|
||||
buf + data + tiff_header_start + 8, alignIntel);
|
||||
this->GeoLocation.LatComponents.seconds = parse_value<Rational>(
|
||||
buf + data + tiff_header_start + 16, alignIntel);
|
||||
this->GeoLocation.Latitude =
|
||||
this->GeoLocation.LatComponents.degrees +
|
||||
this->GeoLocation.LatComponents.minutes / 60 +
|
||||
this->GeoLocation.LatComponents.seconds / 3600;
|
||||
if ('S' == this->GeoLocation.LatComponents.direction) {
|
||||
this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// GPS east or west
|
||||
this->GeoLocation.LonComponents.direction = *(buf + offs + 8);
|
||||
if (this->GeoLocation.LonComponents.direction == 0) {
|
||||
this->GeoLocation.LonComponents.direction = '?';
|
||||
}
|
||||
if ('W' == this->GeoLocation.LonComponents.direction) {
|
||||
this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// GPS longitude
|
||||
if ((format == 5 || format == 10) && length == 3) {
|
||||
this->GeoLocation.LonComponents.degrees = parse_value<Rational>(
|
||||
buf + data + tiff_header_start, alignIntel);
|
||||
this->GeoLocation.LonComponents.minutes = parse_value<Rational>(
|
||||
buf + data + tiff_header_start + 8, alignIntel);
|
||||
this->GeoLocation.LonComponents.seconds = parse_value<Rational>(
|
||||
buf + data + tiff_header_start + 16, alignIntel);
|
||||
this->GeoLocation.Longitude =
|
||||
this->GeoLocation.LonComponents.degrees +
|
||||
this->GeoLocation.LonComponents.minutes / 60 +
|
||||
this->GeoLocation.LonComponents.seconds / 3600;
|
||||
if ('W' == this->GeoLocation.LonComponents.direction)
|
||||
this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// GPS altitude reference (below or above sea level)
|
||||
this->GeoLocation.AltitudeRef = *(buf + offs + 8);
|
||||
if (1 == this->GeoLocation.AltitudeRef) {
|
||||
this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
// GPS altitude
|
||||
if ((format == 5 || format == 10)) {
|
||||
this->GeoLocation.Altitude = parse_value<Rational>(
|
||||
buf + data + tiff_header_start, alignIntel);
|
||||
if (1 == this->GeoLocation.AltitudeRef) {
|
||||
this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 11:
|
||||
// GPS degree of precision (DOP)
|
||||
if ((format == 5 || format == 10)) {
|
||||
this->GeoLocation.DOP = parse_value<Rational>(
|
||||
buf + data + tiff_header_start, alignIntel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
offs += 12;
|
||||
}
|
||||
}
|
||||
|
||||
return PARSE_EXIF_SUCCESS;
|
||||
}
|
||||
|
||||
void easyexif::EXIFInfo::clear() {
|
||||
// Strings
|
||||
ImageDescription = "";
|
||||
Make = "";
|
||||
Model = "";
|
||||
Software = "";
|
||||
DateTime = "";
|
||||
DateTimeOriginal = "";
|
||||
DateTimeDigitized = "";
|
||||
SubSecTimeOriginal = "";
|
||||
Copyright = "";
|
||||
|
||||
// Shorts / unsigned / double
|
||||
ByteAlign = 0;
|
||||
Orientation = 0;
|
||||
|
||||
BitsPerSample = 0;
|
||||
ExposureTime = 0;
|
||||
FNumber = 0;
|
||||
ExposureProgram = 0;
|
||||
ISOSpeedRatings = 0;
|
||||
ShutterSpeedValue = 0;
|
||||
ExposureBiasValue = 0;
|
||||
SubjectDistance = 0;
|
||||
FocalLength = 0;
|
||||
FocalLengthIn35mm = 0;
|
||||
Flash = 0;
|
||||
FlashReturnedLight = 0;
|
||||
FlashMode = 0;
|
||||
MeteringMode = 0;
|
||||
ImageWidth = 0;
|
||||
ImageHeight = 0;
|
||||
|
||||
// Geolocation
|
||||
GeoLocation.Latitude = 0;
|
||||
GeoLocation.Longitude = 0;
|
||||
GeoLocation.Altitude = 0;
|
||||
GeoLocation.AltitudeRef = 0;
|
||||
GeoLocation.DOP = 0;
|
||||
GeoLocation.LatComponents.degrees = 0;
|
||||
GeoLocation.LatComponents.minutes = 0;
|
||||
GeoLocation.LatComponents.seconds = 0;
|
||||
GeoLocation.LatComponents.direction = '?';
|
||||
GeoLocation.LonComponents.degrees = 0;
|
||||
GeoLocation.LonComponents.minutes = 0;
|
||||
GeoLocation.LonComponents.seconds = 0;
|
||||
GeoLocation.LonComponents.direction = '?';
|
||||
|
||||
// LensInfo
|
||||
LensInfo.FocalLengthMax = 0;
|
||||
LensInfo.FocalLengthMin = 0;
|
||||
LensInfo.FStopMax = 0;
|
||||
LensInfo.FStopMin = 0;
|
||||
LensInfo.FocalPlaneYResolution = 0;
|
||||
LensInfo.FocalPlaneXResolution = 0;
|
||||
LensInfo.FocalPlaneResolutionUnit = 0;
|
||||
LensInfo.Make = "";
|
||||
LensInfo.Model = "";
|
||||
}
|
168
exif.h
Normal file
168
exif.h
Normal file
@ -0,0 +1,168 @@
|
||||
/**************************************************************************
|
||||
exif.h -- A simple ISO C++ library to parse basic EXIF
|
||||
information from a JPEG file.
|
||||
|
||||
Based on the description of the EXIF file format at:
|
||||
-- http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
|
||||
-- http://www.media.mit.edu/pia/Research/deepview/exif.html
|
||||
-- http://www.exif.org/Exif2-2.PDF
|
||||
|
||||
Copyright (c) 2010-2016 Mayank Lahiri
|
||||
mlahiri@gmail.com
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
-- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
-- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __EXIF_H
|
||||
#define __EXIF_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace easyexif {
|
||||
|
||||
//
|
||||
// Class responsible for storing and parsing EXIF information from a JPEG blob
|
||||
//
|
||||
class EXIFInfo {
|
||||
public:
|
||||
// Parsing function for an entire JPEG image buffer.
|
||||
//
|
||||
// PARAM 'data': A pointer to a JPEG image.
|
||||
// PARAM 'length': The length of the JPEG image.
|
||||
// RETURN: PARSE_EXIF_SUCCESS (0) on succes with 'result' filled out
|
||||
// error code otherwise, as defined by the PARSE_EXIF_ERROR_* macros
|
||||
int parseFrom(const unsigned char *data, unsigned length);
|
||||
int parseFrom(const std::string &data);
|
||||
|
||||
// Parsing function for an EXIF segment. This is used internally by parseFrom()
|
||||
// but can be called for special cases where only the EXIF section is
|
||||
// available (i.e., a blob starting with the bytes "Exif\0\0").
|
||||
int parseFromEXIFSegment(const unsigned char *buf, unsigned len);
|
||||
|
||||
// Set all data members to default values.
|
||||
void clear();
|
||||
|
||||
// Data fields filled out by parseFrom()
|
||||
char ByteAlign; // 0 = Motorola byte alignment, 1 = Intel
|
||||
std::string ImageDescription; // Image description
|
||||
std::string Make; // Camera manufacturer's name
|
||||
std::string Model; // Camera model
|
||||
unsigned short Orientation; // Image orientation, start of data corresponds to
|
||||
// 0: unspecified in EXIF data
|
||||
// 1: upper left of image
|
||||
// 3: lower right of image
|
||||
// 6: upper right of image
|
||||
// 8: lower left of image
|
||||
// 9: undefined
|
||||
unsigned short BitsPerSample; // Number of bits per component
|
||||
std::string Software; // Software used
|
||||
std::string DateTime; // File change date and time
|
||||
std::string DateTimeOriginal; // Original file date and time (may not exist)
|
||||
std::string DateTimeDigitized; // Digitization date and time (may not exist)
|
||||
std::string SubSecTimeOriginal; // Sub-second time that original picture was taken
|
||||
std::string Copyright; // File copyright information
|
||||
double ExposureTime; // Exposure time in seconds
|
||||
double FNumber; // F/stop
|
||||
unsigned short ExposureProgram; // Exposure program
|
||||
// 0: Not defined
|
||||
// 1: Manual
|
||||
// 2: Normal program
|
||||
// 3: Aperture priority
|
||||
// 4: Shutter priority
|
||||
// 5: Creative program
|
||||
// 6: Action program
|
||||
// 7: Portrait mode
|
||||
// 8: Landscape mode
|
||||
unsigned short ISOSpeedRatings; // ISO speed
|
||||
double ShutterSpeedValue; // Shutter speed (reciprocal of exposure time)
|
||||
double ExposureBiasValue; // Exposure bias value in EV
|
||||
double SubjectDistance; // Distance to focus point in meters
|
||||
double FocalLength; // Focal length of lens in millimeters
|
||||
unsigned short FocalLengthIn35mm; // Focal length in 35mm film
|
||||
char Flash; // 0 = no flash, 1 = flash used
|
||||
unsigned short FlashReturnedLight;// Flash returned light status
|
||||
// 0: No strobe return detection function
|
||||
// 1: Reserved
|
||||
// 2: Strobe return light not detected
|
||||
// 3: Strobe return light detected
|
||||
unsigned short FlashMode; // Flash mode
|
||||
// 0: Unknown
|
||||
// 1: Compulsory flash firing
|
||||
// 2: Compulsory flash suppression
|
||||
// 3: Automatic mode
|
||||
unsigned short MeteringMode; // Metering mode
|
||||
// 1: average
|
||||
// 2: center weighted average
|
||||
// 3: spot
|
||||
// 4: multi-spot
|
||||
// 5: multi-segment
|
||||
unsigned ImageWidth; // Image width reported in EXIF data
|
||||
unsigned ImageHeight; // Image height reported in EXIF data
|
||||
struct Geolocation_t { // GPS information embedded in file
|
||||
double Latitude; // Image latitude expressed as decimal
|
||||
double Longitude; // Image longitude expressed as decimal
|
||||
double Altitude; // Altitude in meters, relative to sea level
|
||||
char AltitudeRef; // 0 = above sea level, -1 = below sea level
|
||||
double DOP; // GPS degree of precision (DOP)
|
||||
struct Coord_t {
|
||||
double degrees;
|
||||
double minutes;
|
||||
double seconds;
|
||||
char direction;
|
||||
} LatComponents, LonComponents; // Latitude, Longitude expressed in deg/min/sec
|
||||
} GeoLocation;
|
||||
struct LensInfo_t { // Lens information
|
||||
double FStopMin; // Min aperture (f-stop)
|
||||
double FStopMax; // Max aperture (f-stop)
|
||||
double FocalLengthMin; // Min focal length (mm)
|
||||
double FocalLengthMax; // Max focal length (mm)
|
||||
double FocalPlaneXResolution; // Focal plane X-resolution
|
||||
double FocalPlaneYResolution; // Focal plane Y-resolution
|
||||
unsigned short FocalPlaneResolutionUnit; // Focal plane resolution unit
|
||||
// 1: No absolute unit of measurement.
|
||||
// 2: Inch.
|
||||
// 3: Centimeter.
|
||||
// 4: Millimeter.
|
||||
// 5: Micrometer.
|
||||
std::string Make; // Lens manufacturer
|
||||
std::string Model; // Lens model
|
||||
} LensInfo;
|
||||
|
||||
|
||||
EXIFInfo() {
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Parse was successful
|
||||
#define PARSE_EXIF_SUCCESS 0
|
||||
// No JPEG markers found in buffer, possibly invalid JPEG file
|
||||
#define PARSE_EXIF_ERROR_NO_JPEG 1982
|
||||
// No EXIF header found in JPEG file.
|
||||
#define PARSE_EXIF_ERROR_NO_EXIF 1983
|
||||
// Byte alignment specified in EXIF file was unknown (not Motorola or Intel).
|
||||
#define PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN 1984
|
||||
// EXIF header was found, but data was corrupted.
|
||||
#define PARSE_EXIF_ERROR_CORRUPT 1985
|
||||
|
||||
#endif
|
302
imageviewer.h
Normal file
302
imageviewer.h
Normal file
@ -0,0 +1,302 @@
|
||||
#ifndef IMGUI_IMAGE_VIEWER_H
|
||||
#define IMGUI_IMAGE_VIEWER_H
|
||||
|
||||
#include "app_image.h" // Needs the AppImage class definition
|
||||
#include "imgui.h"
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS // Allows ImVec2 operators
|
||||
#include "imgui_internal.h" // Need ImFloorSigned, ImClamp, ImMax, ImMin, ImAbs
|
||||
#include <cmath> // For pow, round, floor
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <algorithm> // For std::max, std::min
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS // Allows ImVec2 operators
|
||||
// --- Graphics API ---
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <SDL_opengles2.h>
|
||||
#else
|
||||
#include <SDL_opengl.h>
|
||||
#endif
|
||||
|
||||
|
||||
// --- User Instructions ---
|
||||
// (Same as before: Include after dependencies, define IMPLEMENTATION once)
|
||||
|
||||
class ImGuiImageViewer {
|
||||
public:
|
||||
ImGuiImageViewer();
|
||||
~ImGuiImageViewer();
|
||||
|
||||
ImGuiImageViewer(const ImGuiImageViewer&) = delete;
|
||||
ImGuiImageViewer& operator=(const ImGuiImageViewer&) = delete;
|
||||
|
||||
bool LoadImage(const AppImage& appImage);
|
||||
void UnloadImage();
|
||||
// Renders within the available space, maintaining aspect ratio.
|
||||
void Render(const ImVec2& availableSize);
|
||||
void ResetView(); // Resets zoom to 100%, centers view
|
||||
|
||||
// --- Getters ---
|
||||
float GetZoom() const { return m_zoom; }
|
||||
// Pan offset returns the center of the view in UV coordinates (0.0-1.0)
|
||||
ImVec2 GetPanOffsetUV() const { return m_panOffsetUV; }
|
||||
bool IsImageLoaded() const { return m_textureId != 0; }
|
||||
// Get the raw OpenGL texture ID for external use
|
||||
GLuint GetTextureId() const { return m_textureId; }
|
||||
|
||||
private:
|
||||
GLuint m_textureId = 0;
|
||||
int m_textureWidth = 0;
|
||||
int m_textureHeight = 0;
|
||||
|
||||
float m_zoom = 1.0f; // Scale factor (1.0 = 100%)
|
||||
// Pan offset: Center of the view in UV coordinates (0.0 to 1.0)
|
||||
ImVec2 m_panOffsetUV = ImVec2(0.5f, 0.5f);
|
||||
|
||||
// Interaction state
|
||||
bool m_isDragging = false;
|
||||
ImVec2 m_dragStartMousePos = ImVec2(0, 0);
|
||||
ImVec2 m_dragStartPanOffsetUV = ImVec2(0, 0);
|
||||
|
||||
// Configuration
|
||||
const float m_minZoom = 0.01f; // 1%
|
||||
const float m_maxZoom = 50.0f; // 5000%
|
||||
const float m_zoomSpeed = 1.1f;
|
||||
|
||||
// Clamps pan offset (UV) based on the container size and zoom.
|
||||
void ClampPanOffset(const ImVec2& containerSize);
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// =================== IMPLEMENTATION SECTION =================================
|
||||
// ============================================================================
|
||||
#ifdef IMGUI_IMAGE_VIEWER_IMPLEMENTATION
|
||||
|
||||
// --- Helper namespace ---
|
||||
namespace ImGuiImageViewerUtil {
|
||||
// Linear float [0,1+] -> sRGB approx [0,1]
|
||||
inline float linear_to_srgb_approx(float linearVal) {
|
||||
if (linearVal <= 0.0f) return 0.0f;
|
||||
linearVal = std::fmax(0.0f, std::fmin(1.0f, linearVal)); // Clamp for display
|
||||
if (linearVal <= 0.0031308f) { return linearVal * 12.92f; }
|
||||
else { return 1.055f * std::pow(linearVal, 1.0f / 2.4f) - 0.055f; }
|
||||
}
|
||||
|
||||
// Round float to nearest integer
|
||||
inline float Round(float f) { return ImFloor(f + 0.5f); }
|
||||
} // namespace ImGuiImageViewerUtil
|
||||
|
||||
|
||||
ImGuiImageViewer::ImGuiImageViewer() {}
|
||||
ImGuiImageViewer::~ImGuiImageViewer() { UnloadImage(); }
|
||||
|
||||
void ImGuiImageViewer::UnloadImage() {
|
||||
if (m_textureId != 0) {
|
||||
glDeleteTextures(1, &m_textureId);
|
||||
m_textureId = 0;
|
||||
m_textureWidth = 0;
|
||||
m_textureHeight = 0;
|
||||
ResetView();
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiImageViewer::LoadImage(const AppImage& appImage) {
|
||||
// --- Basic Error Checking ---
|
||||
if (appImage.isEmpty() || appImage.getWidth() == 0 || appImage.getHeight() == 0) {
|
||||
UnloadImage(); return false;
|
||||
}
|
||||
if (!appImage.isLinear()) { /* Log warning */ }
|
||||
const int width = static_cast<int>(appImage.getWidth());
|
||||
const int height = static_cast<int>(appImage.getHeight());
|
||||
const int channels = static_cast<int>(appImage.getChannels());
|
||||
if (channels != 1 && channels != 3 && channels != 4) { UnloadImage(); return false; }
|
||||
const float* linearData = appImage.getData();
|
||||
|
||||
// --- Prepare 8-bit sRGB data for OpenGL (RGBA format) ---
|
||||
GLenum internalFormat = GL_RGBA8; GLenum dataFormat = GL_RGBA; int outputChannels = 4;
|
||||
std::vector<unsigned char> textureData(static_cast<size_t>(width) * height * outputChannels);
|
||||
unsigned char* outPtr = textureData.data(); const float* inPtr = linearData;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
float r=0,g=0,b=0,a=1; if(channels==1){r=g=b=*inPtr++;} else if(channels==3){r=*inPtr++;g=*inPtr++;b=*inPtr++;} else{r=*inPtr++;g=*inPtr++;b=*inPtr++;a=*inPtr++;}
|
||||
float sr=ImGuiImageViewerUtil::linear_to_srgb_approx(r), sg=ImGuiImageViewerUtil::linear_to_srgb_approx(g), sb=ImGuiImageViewerUtil::linear_to_srgb_approx(b); a=std::fmax(0.f,std::fmin(1.f,a));
|
||||
*outPtr++=static_cast<unsigned char>(std::max(0, std::min(255, static_cast<int>(ImGuiImageViewerUtil::Round(sr*255.f)))));
|
||||
*outPtr++=static_cast<unsigned char>(std::max(0, std::min(255, static_cast<int>(ImGuiImageViewerUtil::Round(sg*255.f)))));
|
||||
*outPtr++=static_cast<unsigned char>(std::max(0, std::min(255, static_cast<int>(ImGuiImageViewerUtil::Round(sb*255.f)))));
|
||||
*outPtr++=static_cast<unsigned char>(std::max(0, std::min(255, static_cast<int>(ImGuiImageViewerUtil::Round(a*255.f)))));
|
||||
}
|
||||
}
|
||||
|
||||
// --- Upload to OpenGL Texture ---
|
||||
GLint lastTexture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTexture);
|
||||
if (m_textureId == 0) { glGenTextures(1, &m_textureId); }
|
||||
glBindTexture(GL_TEXTURE_2D, m_textureId);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // No filtering
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // No filtering
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, dataFormat, GL_UNSIGNED_BYTE, textureData.data());
|
||||
glBindTexture(GL_TEXTURE_2D, lastTexture);
|
||||
|
||||
m_textureWidth = width; m_textureHeight = height;
|
||||
ResetView(); // Reset view when loading a new image
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiImageViewer::ResetView() {
|
||||
m_zoom = 1.0f;
|
||||
m_panOffsetUV = ImVec2(0.5f, 0.5f); // Center view
|
||||
m_isDragging = false;
|
||||
}
|
||||
|
||||
// Clamp pan offset (UV coordinates)
|
||||
void ImGuiImageViewer::ClampPanOffset(const ImVec2& containerSize) {
|
||||
if (m_textureWidth <= 0 || m_textureHeight <= 0 || m_zoom <= 0) return;
|
||||
|
||||
// Size of the image content projected onto the screen at current zoom
|
||||
ImVec2 zoomedImageScreenSize = ImVec2(m_textureWidth * m_zoom, m_textureHeight * m_zoom);
|
||||
|
||||
// Calculate the size of the visible area in UV coordinates
|
||||
ImVec2 viewSizeUV = containerSize / zoomedImageScreenSize; // If zoom=1, size=texSize, this is containerSize/texSize
|
||||
viewSizeUV.x = ImMin(viewSizeUV.x, 1.0f); // Cannot see more than 1.0 UV width
|
||||
viewSizeUV.y = ImMin(viewSizeUV.y, 1.0f); // Cannot see more than 1.0 UV height
|
||||
|
||||
// Calculate min/max allowed pan values (center of view in UV space)
|
||||
ImVec2 minPanUV, maxPanUV;
|
||||
|
||||
// If the zoomed image is smaller than the container, center it.
|
||||
// The pan offset (center of view) should be locked to 0.5 UV.
|
||||
if (zoomedImageScreenSize.x <= containerSize.x) {
|
||||
minPanUV.x = maxPanUV.x = 0.5f;
|
||||
} else {
|
||||
// Image is wider: Allow panning.
|
||||
// Min pan: Center of view is half the view-width (in UV) from the left edge (UV=0)
|
||||
minPanUV.x = viewSizeUV.x * 0.5f;
|
||||
// Max pan: Center of view is half the view-width (in UV) from the right edge (UV=1)
|
||||
maxPanUV.x = 1.0f - viewSizeUV.x * 0.5f;
|
||||
}
|
||||
|
||||
if (zoomedImageScreenSize.y <= containerSize.y) {
|
||||
// Image is shorter: Center it.
|
||||
minPanUV.y = maxPanUV.y = 0.5f;
|
||||
} else {
|
||||
// Image is taller: Allow panning.
|
||||
minPanUV.y = viewSizeUV.y * 0.5f;
|
||||
maxPanUV.y = 1.0f - viewSizeUV.y * 0.5f;
|
||||
}
|
||||
|
||||
// Apply clamping
|
||||
m_panOffsetUV.x = ImClamp(m_panOffsetUV.x, minPanUV.x, maxPanUV.x);
|
||||
m_panOffsetUV.y = ImClamp(m_panOffsetUV.y, minPanUV.y, maxPanUV.y);
|
||||
}
|
||||
|
||||
|
||||
void ImGuiImageViewer::Render(const ImVec2& availableSize) {
|
||||
if (m_textureId == 0 || m_textureWidth <= 0 || m_textureHeight <= 0) {
|
||||
ImGui::Dummy(availableSize); return;
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
// Begin a child window to establish a canvas with a background
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.1f, 0.1f, 0.1f, 1.0f)); // Dark background for contrast
|
||||
ImGui::BeginChild("ImageViewerCanvas", availableSize, false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration);
|
||||
|
||||
ImVec2 containerSize = ImGui::GetContentRegionAvail();
|
||||
if (containerSize.x < 1.0f) containerSize.x = 1.0f;
|
||||
if (containerSize.y < 1.0f) containerSize.y = 1.0f;
|
||||
ImVec2 containerTopLeftScreen = ImGui::GetCursorScreenPos();
|
||||
|
||||
// Calculate aspect-correct display size and padding within the container
|
||||
float imageAspect = static_cast<float>(m_textureWidth) / static_cast<float>(m_textureHeight);
|
||||
float containerAspect = containerSize.x / containerSize.y;
|
||||
ImVec2 displaySize = containerSize; // Final screen size of the image content
|
||||
ImVec2 displayPadding = ImVec2(0, 0); // Padding for letter/pillarboxing
|
||||
|
||||
if (containerAspect > imageAspect) { // Letterbox: Container wider than image
|
||||
displaySize.x = containerSize.y * imageAspect;
|
||||
displayPadding.x = (containerSize.x - displaySize.x) * 0.5f;
|
||||
} else { // Pillarbox: Container taller than image (or same aspect)
|
||||
displaySize.y = containerSize.x / imageAspect;
|
||||
displayPadding.y = (containerSize.y - displaySize.y) * 0.5f;
|
||||
}
|
||||
// Round padding to avoid pixel jitter
|
||||
displayPadding.x = ImFloor(displayPadding.x);
|
||||
displayPadding.y = ImFloor(displayPadding.y);
|
||||
displaySize = containerSize - displayPadding * 2.0f; // Recalculate based on rounded padding
|
||||
|
||||
|
||||
// Screen position where the image content rendering starts
|
||||
ImVec2 displayTopLeftScreen = containerTopLeftScreen + displayPadding;
|
||||
|
||||
// Invisible button covering the *entire container* for capturing inputs
|
||||
ImGui::SetCursorScreenPos(containerTopLeftScreen);
|
||||
ImGui::InvisibleButton("##canvas_interaction", containerSize);
|
||||
bool isHoveredOnCanvas = ImGui::IsItemHovered();
|
||||
bool isActiveOnCanvas = ImGui::IsItemActive();
|
||||
|
||||
// Calculate mouse position relative to the *display area* top-left corner
|
||||
ImVec2 mousePosDisplay = io.MousePos - displayTopLeftScreen;
|
||||
|
||||
// Check if the mouse is actually over the image content, not the padding
|
||||
bool isMouseInDisplayArea = (mousePosDisplay.x >= 0.0f && mousePosDisplay.x < displaySize.x &&
|
||||
mousePosDisplay.y >= 0.0f && mousePosDisplay.y < displaySize.y);
|
||||
|
||||
// 1. Zooming (centered on mouse, only when hovering the image content)
|
||||
if (isHoveredOnCanvas && isMouseInDisplayArea && io.MouseWheel != 0.0f) {
|
||||
// Mouse position relative to display center, in screen pixels
|
||||
ImVec2 mouseRelCenter = mousePosDisplay - displaySize * 0.5f;
|
||||
// Convert mouse position to UV coordinates *before* zoom change
|
||||
ImVec2 mouseUV = m_panOffsetUV + mouseRelCenter / (ImVec2((float)m_textureWidth, (float)m_textureHeight) * m_zoom);
|
||||
|
||||
float oldZoom = m_zoom;
|
||||
m_zoom *= std::pow(m_zoomSpeed, io.MouseWheel);
|
||||
m_zoom = ImClamp(m_zoom, m_minZoom, m_maxZoom); // Clamp zoom
|
||||
|
||||
// Keep the same UV coordinate under the mouse cursor after zoom
|
||||
// NewPan = MouseUV - (MouseScreenPosRelCenter / NewZoomedImageScreenSize)
|
||||
m_panOffsetUV = mouseUV - mouseRelCenter / (ImVec2((float)m_textureWidth, (float)m_textureHeight) * m_zoom);
|
||||
|
||||
ClampPanOffset(containerSize); // Re-clamp after zoom
|
||||
}
|
||||
|
||||
// 2. Panning (allow dragging anywhere on the canvas)
|
||||
if (isActiveOnCanvas && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
if (!m_isDragging) {
|
||||
m_isDragging = true;
|
||||
m_dragStartMousePos = io.MousePos;
|
||||
m_dragStartPanOffsetUV = m_panOffsetUV;
|
||||
}
|
||||
ImVec2 mouseDelta = io.MousePos - m_dragStartMousePos;
|
||||
// Convert screen pixel delta to UV delta
|
||||
ImVec2 uvDelta = mouseDelta / (ImVec2((float)m_textureWidth, (float)m_textureHeight) * m_zoom);
|
||||
|
||||
m_panOffsetUV = m_dragStartPanOffsetUV - uvDelta; // Subtract because dragging right moves content left (reduces UV offset)
|
||||
|
||||
ClampPanOffset(containerSize); // Clamp during drag
|
||||
|
||||
} else if (m_isDragging) {
|
||||
m_isDragging = false;
|
||||
}
|
||||
|
||||
// --- UV Calculation ---
|
||||
// Calculate the size of the viewable rectangle in UV coordinates
|
||||
ImVec2 viewSizeUV = displaySize / (ImVec2((float)m_textureWidth, (float)m_textureHeight) * m_zoom);
|
||||
// Calculate top-left (uv0) and bottom-right (uv1) UV coordinates based on the centered pan offset
|
||||
ImVec2 uv0 = m_panOffsetUV - viewSizeUV * 0.5f;
|
||||
ImVec2 uv1 = m_panOffsetUV + viewSizeUV * 0.5f;
|
||||
|
||||
// --- Draw the Image ---
|
||||
ImGui::SetCursorScreenPos(displayTopLeftScreen); // Position cursor for image drawing
|
||||
ImGui::Image((ImTextureID)(intptr_t)m_textureId, displaySize, uv0, uv1);
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor(); // Pop ChildBg color
|
||||
}
|
||||
|
||||
#endif // IMGUI_IMAGE_VIEWER_IMPLEMENTATION
|
||||
|
||||
#endif // IMGUI_IMAGE_VIEWER_H
|
141
imconfig.h
Normal file
141
imconfig.h
Normal file
@ -0,0 +1,141 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||
//-----------------------------------------------------------------------------
|
||||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||
|
||||
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||
//#define IMGUI_USE_STB_SPRINTF
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||
/*
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
|
||||
operator MyVec2() const { return MyVec2(x,y); }
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
|
||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||
*/
|
||||
//---- ...Or use Dear ImGui's own very basic math operators.
|
||||
//#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||
//#define ImDrawIdx unsigned int
|
||||
|
||||
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||
//struct ImDrawList;
|
||||
//struct ImDrawCmd;
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||
}
|
||||
*/
|
1334
imfilebrowser.h
Normal file
1334
imfilebrowser.h
Normal file
File diff suppressed because it is too large
Load Diff
270
imgui.ini
Normal file
270
imgui.ini
Normal file
@ -0,0 +1,270 @@
|
||||
[Window][Image Exif]
|
||||
Pos=0,19
|
||||
Size=501,2072
|
||||
Collapsed=0
|
||||
DockId=0x00000003,0
|
||||
|
||||
[Window][Edit Image]
|
||||
Pos=3563,19
|
||||
Size=277,2072
|
||||
Collapsed=0
|
||||
DockId=0x00000002,0
|
||||
|
||||
[Window][Image View]
|
||||
Pos=503,19
|
||||
Size=3058,2072
|
||||
Collapsed=0
|
||||
DockId=0x00000004,0
|
||||
|
||||
[Window][DockSpaceWindowHost]
|
||||
Pos=0,19
|
||||
Size=3840,2072
|
||||
Collapsed=0
|
||||
|
||||
[Window][Debug##Default]
|
||||
Pos=60,60
|
||||
Size=400,400
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_104234674021344]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_103820503331776]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Export Settings]
|
||||
Pos=1779,983
|
||||
Size=281,125
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_98063134548928]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Export Image As##filebrowser_98063134549408]
|
||||
Pos=1570,820
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_105833422020672]
|
||||
Pos=483,342
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Export Image As##filebrowser_105833422020192]
|
||||
Pos=483,342
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_106326659371072]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Export Image As##filebrowser_106326659370592]
|
||||
Pos=290,136
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_105277128065088]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_95064985587904]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_102890187944128]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_106831463805120]
|
||||
Pos=290,137
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_107663186985152]
|
||||
Pos=1570,820
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Export Image As##filebrowser_107663186984672]
|
||||
Pos=1570,820
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_107790165545152]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_109207766827200]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_103700153057472]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_98304290763072]
|
||||
Pos=1570,820
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_101853495222592]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_95128140845376]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_106527524852032]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_98089467183424]
|
||||
Pos=1570,820
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Export Image As##filebrowser_98089467182944]
|
||||
Pos=1570,820
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_104625190056256]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Export Image As##filebrowser_104625190055776]
|
||||
Pos=1570,820
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_104822115266880]
|
||||
Pos=419,435
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_94123613466944]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_95503940182336]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_101453941682496]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_95384985870656]
|
||||
Pos=610,820
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_102433741902144]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_110281562521920]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_102264946090304]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_94907470143808]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_95449924194624]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_102446271447360]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_96827416379712]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_97775434105152]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_109659660910912]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_106451793555776]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_111077646164288]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_105124544713024]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_101628419936576]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_101417705099584]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_106953443598656]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Open Image File##filebrowser_106456350257472]
|
||||
Pos=290,135
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0xE098E157 Window=0x9E772337 Pos=0,19 Size=3840,2072 Split=X
|
||||
DockNode ID=0x00000001 Parent=0xE098E157 SizeRef=1641,720 Split=X
|
||||
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=501,720 Selected=0x5593B2D4
|
||||
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=3058,720 CentralNode=1 Selected=0x9B39CB70
|
||||
DockNode ID=0x00000002 Parent=0xE098E157 SizeRef=277,720 Selected=0x610DAB84
|
||||
|
10984
imgui_demo.cpp
Normal file
10984
imgui_demo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
4857
imgui_draw.cpp
Normal file
4857
imgui_draw.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1023
imgui_impl_opengl3.cpp
Normal file
1023
imgui_impl_opengl3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
67
imgui_impl_opengl3.h
Normal file
67
imgui_impl_opengl3.h
Normal file
@ -0,0 +1,67 @@
|
||||
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
||||
// - Desktop GL: 2.x 3.x 4.x
|
||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]
|
||||
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||
|
||||
// About WebGL/ES:
|
||||
// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.
|
||||
// - This is done automatically on iOS, Android and Emscripten targets.
|
||||
// - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
// About GLSL version:
|
||||
// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string.
|
||||
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
|
||||
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// (Optional) Called by Init/NewFrame/Shutdown
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||
|
||||
// Configuration flags to add in your imconfig file:
|
||||
//#define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten)
|
||||
//#define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android)
|
||||
|
||||
// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
|
||||
// Try to detect GLES on matching platforms
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
|
||||
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
|
||||
#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__)
|
||||
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
|
||||
#else
|
||||
// Otherwise imgui_impl_opengl3_loader.h will be used.
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
916
imgui_impl_opengl3_loader.h
Normal file
916
imgui_impl_opengl3_loader.h
Normal file
@ -0,0 +1,916 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// About imgui_impl_opengl3_loader.h:
|
||||
//
|
||||
// We embed our own OpenGL loader to not require user to provide their own or to have to use ours,
|
||||
// which proved to be endless problems for users.
|
||||
// Our loader is custom-generated, based on gl3w but automatically filtered to only include
|
||||
// enums/functions that we use in our imgui_impl_opengl3.cpp source file in order to be small.
|
||||
//
|
||||
// YOU SHOULD NOT NEED TO INCLUDE/USE THIS DIRECTLY. THIS IS USED BY imgui_impl_opengl3.cpp ONLY.
|
||||
// THE REST OF YOUR APP SHOULD USE A DIFFERENT GL LOADER: ANY GL LOADER OF YOUR CHOICE.
|
||||
//
|
||||
// IF YOU GET BUILD ERRORS IN THIS FILE (commonly macro redefinitions or function redefinitions):
|
||||
// IT LIKELY MEANS THAT YOU ARE BUILDING 'imgui_impl_opengl3.cpp' OR INCLUDING 'imgui_impl_opengl3_loader.h'
|
||||
// IN THE SAME COMPILATION UNIT AS ONE OF YOUR FILE WHICH IS USING A THIRD-PARTY OPENGL LOADER.
|
||||
// (e.g. COULD HAPPEN IF YOU ARE DOING A UNITY/JUMBO BUILD, OR INCLUDING .CPP FILES FROM OTHERS)
|
||||
// YOU SHOULD NOT BUILD BOTH IN THE SAME COMPILATION UNIT.
|
||||
// BUT IF YOU REALLY WANT TO, you can '#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM' and imgui_impl_opengl3.cpp
|
||||
// WILL NOT BE USING OUR LOADER, AND INSTEAD EXPECT ANOTHER/YOUR LOADER TO BE AVAILABLE IN THE COMPILATION UNIT.
|
||||
//
|
||||
// Regenerate with:
|
||||
// python3 gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
|
||||
//
|
||||
// More info:
|
||||
// https://github.com/dearimgui/gl3w_stripped
|
||||
// https://github.com/ocornut/imgui/issues/4445
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* This file was generated with gl3w_gen.py, part of imgl3w
|
||||
* (hosted at https://github.com/dearimgui/gl3w_stripped)
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*
|
||||
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
* distribute this software, either in source code form or as a compiled
|
||||
* binary, for any purpose, commercial or non-commercial, and by any
|
||||
* means.
|
||||
*
|
||||
* In jurisdictions that recognize copyright laws, the author or authors
|
||||
* of this software dedicate any and all copyright interest in the
|
||||
* software to the public domain. We make this dedication for the benefit
|
||||
* of the public at large and to the detriment of our heirs and
|
||||
* successors. We intend this dedication to be an overt act of
|
||||
* relinquishment in perpetuity of all present and future rights to this
|
||||
* software under copyright law.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __gl3w_h_
|
||||
#define __gl3w_h_
|
||||
|
||||
// Adapted from KHR/khrplatform.h to avoid including entire file.
|
||||
#ifndef __khrplatform_h_
|
||||
typedef float khronos_float_t;
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
#ifdef _WIN64
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef signed long int khronos_ssize_t;
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
typedef signed __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100)
|
||||
#include <stdint.h>
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#else
|
||||
typedef signed long long khronos_int64_t;
|
||||
typedef unsigned long long khronos_uint64_t;
|
||||
#endif
|
||||
#endif // __khrplatform_h_
|
||||
|
||||
#ifndef __gl_glcorearb_h_
|
||||
#define __gl_glcorearb_h_ 1
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
** Copyright 2013-2020 The Khronos Group Inc.
|
||||
** SPDX-License-Identifier: MIT
|
||||
**
|
||||
** This header is generated from the Khronos OpenGL / OpenGL ES XML
|
||||
** API Registry. The current version of the Registry, generator scripts
|
||||
** used to make the header, and the header can be found at
|
||||
** https://github.com/KhronosGroup/OpenGL-Registry
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#ifndef APIENTRY
|
||||
#define APIENTRY
|
||||
#endif
|
||||
#ifndef APIENTRYP
|
||||
#define APIENTRYP APIENTRY *
|
||||
#endif
|
||||
#ifndef GLAPI
|
||||
#define GLAPI extern
|
||||
#endif
|
||||
/* glcorearb.h is for use with OpenGL core profile implementations.
|
||||
** It should should be placed in the same directory as gl.h and
|
||||
** included as <GL/glcorearb.h>.
|
||||
**
|
||||
** glcorearb.h includes only APIs in the latest OpenGL core profile
|
||||
** implementation together with APIs in newer ARB extensions which
|
||||
** can be supported by the core profile. It does not, and never will
|
||||
** include functionality removed from the core profile, such as
|
||||
** fixed-function vertex and fragment processing.
|
||||
**
|
||||
** Do not #include both <GL/glcorearb.h> and either of <GL/gl.h> or
|
||||
** <GL/glext.h> in the same source file.
|
||||
*/
|
||||
/* Generated C header for:
|
||||
* API: gl
|
||||
* Profile: core
|
||||
* Versions considered: .*
|
||||
* Versions emitted: .*
|
||||
* Default extensions included: glcore
|
||||
* Additional extensions included: _nomatch_^
|
||||
* Extensions removed: _nomatch_^
|
||||
*/
|
||||
#ifndef GL_VERSION_1_0
|
||||
typedef void GLvoid;
|
||||
typedef unsigned int GLenum;
|
||||
|
||||
typedef khronos_float_t GLfloat;
|
||||
typedef int GLint;
|
||||
typedef int GLsizei;
|
||||
typedef unsigned int GLbitfield;
|
||||
typedef double GLdouble;
|
||||
typedef unsigned int GLuint;
|
||||
typedef unsigned char GLboolean;
|
||||
typedef khronos_uint8_t GLubyte;
|
||||
#define GL_COLOR_BUFFER_BIT 0x00004000
|
||||
#define GL_FALSE 0
|
||||
#define GL_TRUE 1
|
||||
#define GL_TRIANGLES 0x0004
|
||||
#define GL_ONE 1
|
||||
#define GL_SRC_ALPHA 0x0302
|
||||
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
|
||||
#define GL_FRONT 0x0404
|
||||
#define GL_BACK 0x0405
|
||||
#define GL_FRONT_AND_BACK 0x0408
|
||||
#define GL_POLYGON_MODE 0x0B40
|
||||
#define GL_CULL_FACE 0x0B44
|
||||
#define GL_DEPTH_TEST 0x0B71
|
||||
#define GL_STENCIL_TEST 0x0B90
|
||||
#define GL_VIEWPORT 0x0BA2
|
||||
#define GL_BLEND 0x0BE2
|
||||
#define GL_SCISSOR_BOX 0x0C10
|
||||
#define GL_SCISSOR_TEST 0x0C11
|
||||
#define GL_UNPACK_ROW_LENGTH 0x0CF2
|
||||
#define GL_PACK_ALIGNMENT 0x0D05
|
||||
#define GL_TEXTURE_2D 0x0DE1
|
||||
#define GL_UNSIGNED_BYTE 0x1401
|
||||
#define GL_UNSIGNED_SHORT 0x1403
|
||||
#define GL_UNSIGNED_INT 0x1405
|
||||
#define GL_FLOAT 0x1406
|
||||
#define GL_RGBA 0x1908
|
||||
#define GL_FILL 0x1B02
|
||||
#define GL_VENDOR 0x1F00
|
||||
#define GL_RENDERER 0x1F01
|
||||
#define GL_VERSION 0x1F02
|
||||
#define GL_EXTENSIONS 0x1F03
|
||||
#define GL_LINEAR 0x2601
|
||||
#define GL_TEXTURE_MAG_FILTER 0x2800
|
||||
#define GL_TEXTURE_MIN_FILTER 0x2801
|
||||
#define GL_TEXTURE_WRAP_S 0x2802
|
||||
#define GL_TEXTURE_WRAP_T 0x2803
|
||||
#define GL_REPEAT 0x2901
|
||||
typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode);
|
||||
typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
|
||||
typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
|
||||
typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask);
|
||||
typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
|
||||
typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap);
|
||||
typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap);
|
||||
typedef void (APIENTRYP PFNGLFLUSHPROC) (void);
|
||||
typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param);
|
||||
typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
|
||||
typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void);
|
||||
typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data);
|
||||
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name);
|
||||
typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap);
|
||||
typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode);
|
||||
GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param);
|
||||
GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
|
||||
GLAPI void APIENTRY glClear (GLbitfield mask);
|
||||
GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
|
||||
GLAPI void APIENTRY glDisable (GLenum cap);
|
||||
GLAPI void APIENTRY glEnable (GLenum cap);
|
||||
GLAPI void APIENTRY glFlush (void);
|
||||
GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param);
|
||||
GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
|
||||
GLAPI GLenum APIENTRY glGetError (void);
|
||||
GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data);
|
||||
GLAPI const GLubyte *APIENTRY glGetString (GLenum name);
|
||||
GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap);
|
||||
GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_0 */
|
||||
#ifndef GL_VERSION_1_1
|
||||
typedef khronos_float_t GLclampf;
|
||||
typedef double GLclampd;
|
||||
#define GL_TEXTURE_BINDING_2D 0x8069
|
||||
typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices);
|
||||
typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
|
||||
typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures);
|
||||
typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);
|
||||
GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture);
|
||||
GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures);
|
||||
GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_1 */
|
||||
#ifndef GL_VERSION_1_2
|
||||
#define GL_CLAMP_TO_EDGE 0x812F
|
||||
#endif /* GL_VERSION_1_2 */
|
||||
#ifndef GL_VERSION_1_3
|
||||
#define GL_TEXTURE0 0x84C0
|
||||
#define GL_ACTIVE_TEXTURE 0x84E0
|
||||
typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glActiveTexture (GLenum texture);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_3 */
|
||||
#ifndef GL_VERSION_1_4
|
||||
#define GL_BLEND_DST_RGB 0x80C8
|
||||
#define GL_BLEND_SRC_RGB 0x80C9
|
||||
#define GL_BLEND_DST_ALPHA 0x80CA
|
||||
#define GL_BLEND_SRC_ALPHA 0x80CB
|
||||
#define GL_FUNC_ADD 0x8006
|
||||
typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
|
||||
typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
|
||||
GLAPI void APIENTRY glBlendEquation (GLenum mode);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_4 */
|
||||
#ifndef GL_VERSION_1_5
|
||||
typedef khronos_ssize_t GLsizeiptr;
|
||||
typedef khronos_intptr_t GLintptr;
|
||||
#define GL_ARRAY_BUFFER 0x8892
|
||||
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
|
||||
#define GL_ARRAY_BUFFER_BINDING 0x8894
|
||||
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
|
||||
#define GL_STREAM_DRAW 0x88E0
|
||||
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
|
||||
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
|
||||
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
|
||||
typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
|
||||
typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer);
|
||||
GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers);
|
||||
GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers);
|
||||
GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
|
||||
GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_5 */
|
||||
#ifndef GL_VERSION_2_0
|
||||
typedef char GLchar;
|
||||
typedef khronos_int16_t GLshort;
|
||||
typedef khronos_int8_t GLbyte;
|
||||
typedef khronos_uint16_t GLushort;
|
||||
#define GL_BLEND_EQUATION_RGB 0x8009
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
|
||||
#define GL_BLEND_EQUATION_ALPHA 0x883D
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
|
||||
#define GL_FRAGMENT_SHADER 0x8B30
|
||||
#define GL_VERTEX_SHADER 0x8B31
|
||||
#define GL_COMPILE_STATUS 0x8B81
|
||||
#define GL_LINK_STATUS 0x8B82
|
||||
#define GL_INFO_LOG_LENGTH 0x8B84
|
||||
#define GL_CURRENT_PROGRAM 0x8B8D
|
||||
#define GL_UPPER_LEFT 0x8CA2
|
||||
typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha);
|
||||
typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
|
||||
typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader);
|
||||
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void);
|
||||
typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type);
|
||||
typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program);
|
||||
typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader);
|
||||
typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader);
|
||||
typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index);
|
||||
typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
|
||||
typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
|
||||
typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
|
||||
typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
|
||||
typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
|
||||
typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
|
||||
typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params);
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer);
|
||||
typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program);
|
||||
typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program);
|
||||
typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
|
||||
typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);
|
||||
typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
|
||||
typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha);
|
||||
GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader);
|
||||
GLAPI void APIENTRY glCompileShader (GLuint shader);
|
||||
GLAPI GLuint APIENTRY glCreateProgram (void);
|
||||
GLAPI GLuint APIENTRY glCreateShader (GLenum type);
|
||||
GLAPI void APIENTRY glDeleteProgram (GLuint program);
|
||||
GLAPI void APIENTRY glDeleteShader (GLuint shader);
|
||||
GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader);
|
||||
GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index);
|
||||
GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index);
|
||||
GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name);
|
||||
GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params);
|
||||
GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
|
||||
GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params);
|
||||
GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
|
||||
GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name);
|
||||
GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params);
|
||||
GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer);
|
||||
GLAPI GLboolean APIENTRY glIsProgram (GLuint program);
|
||||
GLAPI void APIENTRY glLinkProgram (GLuint program);
|
||||
GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
|
||||
GLAPI void APIENTRY glUseProgram (GLuint program);
|
||||
GLAPI void APIENTRY glUniform1i (GLint location, GLint v0);
|
||||
GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
|
||||
#endif
|
||||
#endif /* GL_VERSION_2_0 */
|
||||
#ifndef GL_VERSION_2_1
|
||||
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
|
||||
#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
|
||||
#endif /* GL_VERSION_2_1 */
|
||||
#ifndef GL_VERSION_3_0
|
||||
typedef khronos_uint16_t GLhalf;
|
||||
#define GL_MAJOR_VERSION 0x821B
|
||||
#define GL_MINOR_VERSION 0x821C
|
||||
#define GL_NUM_EXTENSIONS 0x821D
|
||||
#define GL_FRAMEBUFFER_SRGB 0x8DB9
|
||||
#define GL_VERTEX_ARRAY_BINDING 0x85B5
|
||||
typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data);
|
||||
typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data);
|
||||
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index);
|
||||
typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array);
|
||||
typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);
|
||||
typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index);
|
||||
GLAPI void APIENTRY glBindVertexArray (GLuint array);
|
||||
GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays);
|
||||
GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays);
|
||||
#endif
|
||||
#endif /* GL_VERSION_3_0 */
|
||||
#ifndef GL_VERSION_3_1
|
||||
#define GL_VERSION_3_1 1
|
||||
#define GL_PRIMITIVE_RESTART 0x8F9D
|
||||
#endif /* GL_VERSION_3_1 */
|
||||
#ifndef GL_VERSION_3_2
|
||||
#define GL_VERSION_3_2 1
|
||||
typedef struct __GLsync *GLsync;
|
||||
typedef khronos_uint64_t GLuint64;
|
||||
typedef khronos_int64_t GLint64;
|
||||
#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
|
||||
#define GL_CONTEXT_PROFILE_MASK 0x9126
|
||||
typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
|
||||
typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
|
||||
#endif
|
||||
#endif /* GL_VERSION_3_2 */
|
||||
#ifndef GL_VERSION_3_3
|
||||
#define GL_VERSION_3_3 1
|
||||
#define GL_SAMPLER_BINDING 0x8919
|
||||
typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler);
|
||||
#endif
|
||||
#endif /* GL_VERSION_3_3 */
|
||||
#ifndef GL_VERSION_4_1
|
||||
typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data);
|
||||
typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data);
|
||||
#endif /* GL_VERSION_4_1 */
|
||||
#ifndef GL_VERSION_4_3
|
||||
typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
#endif /* GL_VERSION_4_3 */
|
||||
#ifndef GL_VERSION_4_5
|
||||
#define GL_CLIP_ORIGIN 0x935C
|
||||
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param);
|
||||
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param);
|
||||
#endif /* GL_VERSION_4_5 */
|
||||
#ifndef GL_ARB_bindless_texture
|
||||
typedef khronos_uint64_t GLuint64EXT;
|
||||
#endif /* GL_ARB_bindless_texture */
|
||||
#ifndef GL_ARB_cl_event
|
||||
struct _cl_context;
|
||||
struct _cl_event;
|
||||
#endif /* GL_ARB_cl_event */
|
||||
#ifndef GL_ARB_clip_control
|
||||
#define GL_ARB_clip_control 1
|
||||
#endif /* GL_ARB_clip_control */
|
||||
#ifndef GL_ARB_debug_output
|
||||
typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
#endif /* GL_ARB_debug_output */
|
||||
#ifndef GL_EXT_EGL_image_storage
|
||||
typedef void *GLeglImageOES;
|
||||
#endif /* GL_EXT_EGL_image_storage */
|
||||
#ifndef GL_EXT_direct_state_access
|
||||
typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params);
|
||||
typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params);
|
||||
typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params);
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param);
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param);
|
||||
#endif /* GL_EXT_direct_state_access */
|
||||
#ifndef GL_NV_draw_vulkan_image
|
||||
typedef void (APIENTRY *GLVULKANPROCNV)(void);
|
||||
#endif /* GL_NV_draw_vulkan_image */
|
||||
#ifndef GL_NV_gpu_shader5
|
||||
typedef khronos_int64_t GLint64EXT;
|
||||
#endif /* GL_NV_gpu_shader5 */
|
||||
#ifndef GL_NV_vertex_buffer_unified_memory
|
||||
typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result);
|
||||
#endif /* GL_NV_vertex_buffer_unified_memory */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GL3W_API
|
||||
#define GL3W_API
|
||||
#endif
|
||||
|
||||
#ifndef __gl_h_
|
||||
#define __gl_h_
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define GL3W_OK 0
|
||||
#define GL3W_ERROR_INIT -1
|
||||
#define GL3W_ERROR_LIBRARY_OPEN -2
|
||||
#define GL3W_ERROR_OPENGL_VERSION -3
|
||||
|
||||
typedef void (*GL3WglProc)(void);
|
||||
typedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc);
|
||||
|
||||
/* gl3w api */
|
||||
GL3W_API int imgl3wInit(void);
|
||||
GL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc);
|
||||
GL3W_API int imgl3wIsSupported(int major, int minor);
|
||||
GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
|
||||
|
||||
/* gl3w internal state */
|
||||
union ImGL3WProcs {
|
||||
GL3WglProc ptr[59];
|
||||
struct {
|
||||
PFNGLACTIVETEXTUREPROC ActiveTexture;
|
||||
PFNGLATTACHSHADERPROC AttachShader;
|
||||
PFNGLBINDBUFFERPROC BindBuffer;
|
||||
PFNGLBINDSAMPLERPROC BindSampler;
|
||||
PFNGLBINDTEXTUREPROC BindTexture;
|
||||
PFNGLBINDVERTEXARRAYPROC BindVertexArray;
|
||||
PFNGLBLENDEQUATIONPROC BlendEquation;
|
||||
PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate;
|
||||
PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate;
|
||||
PFNGLBUFFERDATAPROC BufferData;
|
||||
PFNGLBUFFERSUBDATAPROC BufferSubData;
|
||||
PFNGLCLEARPROC Clear;
|
||||
PFNGLCLEARCOLORPROC ClearColor;
|
||||
PFNGLCOMPILESHADERPROC CompileShader;
|
||||
PFNGLCREATEPROGRAMPROC CreateProgram;
|
||||
PFNGLCREATESHADERPROC CreateShader;
|
||||
PFNGLDELETEBUFFERSPROC DeleteBuffers;
|
||||
PFNGLDELETEPROGRAMPROC DeleteProgram;
|
||||
PFNGLDELETESHADERPROC DeleteShader;
|
||||
PFNGLDELETETEXTURESPROC DeleteTextures;
|
||||
PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays;
|
||||
PFNGLDETACHSHADERPROC DetachShader;
|
||||
PFNGLDISABLEPROC Disable;
|
||||
PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray;
|
||||
PFNGLDRAWELEMENTSPROC DrawElements;
|
||||
PFNGLDRAWELEMENTSBASEVERTEXPROC DrawElementsBaseVertex;
|
||||
PFNGLENABLEPROC Enable;
|
||||
PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray;
|
||||
PFNGLFLUSHPROC Flush;
|
||||
PFNGLGENBUFFERSPROC GenBuffers;
|
||||
PFNGLGENTEXTURESPROC GenTextures;
|
||||
PFNGLGENVERTEXARRAYSPROC GenVertexArrays;
|
||||
PFNGLGETATTRIBLOCATIONPROC GetAttribLocation;
|
||||
PFNGLGETERRORPROC GetError;
|
||||
PFNGLGETINTEGERVPROC GetIntegerv;
|
||||
PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog;
|
||||
PFNGLGETPROGRAMIVPROC GetProgramiv;
|
||||
PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog;
|
||||
PFNGLGETSHADERIVPROC GetShaderiv;
|
||||
PFNGLGETSTRINGPROC GetString;
|
||||
PFNGLGETSTRINGIPROC GetStringi;
|
||||
PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation;
|
||||
PFNGLGETVERTEXATTRIBPOINTERVPROC GetVertexAttribPointerv;
|
||||
PFNGLGETVERTEXATTRIBIVPROC GetVertexAttribiv;
|
||||
PFNGLISENABLEDPROC IsEnabled;
|
||||
PFNGLISPROGRAMPROC IsProgram;
|
||||
PFNGLLINKPROGRAMPROC LinkProgram;
|
||||
PFNGLPIXELSTOREIPROC PixelStorei;
|
||||
PFNGLPOLYGONMODEPROC PolygonMode;
|
||||
PFNGLREADPIXELSPROC ReadPixels;
|
||||
PFNGLSCISSORPROC Scissor;
|
||||
PFNGLSHADERSOURCEPROC ShaderSource;
|
||||
PFNGLTEXIMAGE2DPROC TexImage2D;
|
||||
PFNGLTEXPARAMETERIPROC TexParameteri;
|
||||
PFNGLUNIFORM1IPROC Uniform1i;
|
||||
PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv;
|
||||
PFNGLUSEPROGRAMPROC UseProgram;
|
||||
PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer;
|
||||
PFNGLVIEWPORTPROC Viewport;
|
||||
} gl;
|
||||
};
|
||||
|
||||
GL3W_API extern union ImGL3WProcs imgl3wProcs;
|
||||
|
||||
/* OpenGL functions */
|
||||
#define glActiveTexture imgl3wProcs.gl.ActiveTexture
|
||||
#define glAttachShader imgl3wProcs.gl.AttachShader
|
||||
#define glBindBuffer imgl3wProcs.gl.BindBuffer
|
||||
#define glBindSampler imgl3wProcs.gl.BindSampler
|
||||
#define glBindTexture imgl3wProcs.gl.BindTexture
|
||||
#define glBindVertexArray imgl3wProcs.gl.BindVertexArray
|
||||
#define glBlendEquation imgl3wProcs.gl.BlendEquation
|
||||
#define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate
|
||||
#define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate
|
||||
#define glBufferData imgl3wProcs.gl.BufferData
|
||||
#define glBufferSubData imgl3wProcs.gl.BufferSubData
|
||||
#define glClear imgl3wProcs.gl.Clear
|
||||
#define glClearColor imgl3wProcs.gl.ClearColor
|
||||
#define glCompileShader imgl3wProcs.gl.CompileShader
|
||||
#define glCreateProgram imgl3wProcs.gl.CreateProgram
|
||||
#define glCreateShader imgl3wProcs.gl.CreateShader
|
||||
#define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers
|
||||
#define glDeleteProgram imgl3wProcs.gl.DeleteProgram
|
||||
#define glDeleteShader imgl3wProcs.gl.DeleteShader
|
||||
#define glDeleteTextures imgl3wProcs.gl.DeleteTextures
|
||||
#define glDeleteVertexArrays imgl3wProcs.gl.DeleteVertexArrays
|
||||
#define glDetachShader imgl3wProcs.gl.DetachShader
|
||||
#define glDisable imgl3wProcs.gl.Disable
|
||||
#define glDisableVertexAttribArray imgl3wProcs.gl.DisableVertexAttribArray
|
||||
#define glDrawElements imgl3wProcs.gl.DrawElements
|
||||
#define glDrawElementsBaseVertex imgl3wProcs.gl.DrawElementsBaseVertex
|
||||
#define glEnable imgl3wProcs.gl.Enable
|
||||
#define glEnableVertexAttribArray imgl3wProcs.gl.EnableVertexAttribArray
|
||||
#define glFlush imgl3wProcs.gl.Flush
|
||||
#define glGenBuffers imgl3wProcs.gl.GenBuffers
|
||||
#define glGenTextures imgl3wProcs.gl.GenTextures
|
||||
#define glGenVertexArrays imgl3wProcs.gl.GenVertexArrays
|
||||
#define glGetAttribLocation imgl3wProcs.gl.GetAttribLocation
|
||||
#define glGetError imgl3wProcs.gl.GetError
|
||||
#define glGetIntegerv imgl3wProcs.gl.GetIntegerv
|
||||
#define glGetProgramInfoLog imgl3wProcs.gl.GetProgramInfoLog
|
||||
#define glGetProgramiv imgl3wProcs.gl.GetProgramiv
|
||||
#define glGetShaderInfoLog imgl3wProcs.gl.GetShaderInfoLog
|
||||
#define glGetShaderiv imgl3wProcs.gl.GetShaderiv
|
||||
#define glGetString imgl3wProcs.gl.GetString
|
||||
#define glGetStringi imgl3wProcs.gl.GetStringi
|
||||
#define glGetUniformLocation imgl3wProcs.gl.GetUniformLocation
|
||||
#define glGetVertexAttribPointerv imgl3wProcs.gl.GetVertexAttribPointerv
|
||||
#define glGetVertexAttribiv imgl3wProcs.gl.GetVertexAttribiv
|
||||
#define glIsEnabled imgl3wProcs.gl.IsEnabled
|
||||
#define glIsProgram imgl3wProcs.gl.IsProgram
|
||||
#define glLinkProgram imgl3wProcs.gl.LinkProgram
|
||||
#define glPixelStorei imgl3wProcs.gl.PixelStorei
|
||||
#define glPolygonMode imgl3wProcs.gl.PolygonMode
|
||||
#define glReadPixels imgl3wProcs.gl.ReadPixels
|
||||
#define glScissor imgl3wProcs.gl.Scissor
|
||||
#define glShaderSource imgl3wProcs.gl.ShaderSource
|
||||
#define glTexImage2D imgl3wProcs.gl.TexImage2D
|
||||
#define glTexParameteri imgl3wProcs.gl.TexParameteri
|
||||
#define glUniform1i imgl3wProcs.gl.Uniform1i
|
||||
#define glUniformMatrix4fv imgl3wProcs.gl.UniformMatrix4fv
|
||||
#define glUseProgram imgl3wProcs.gl.UseProgram
|
||||
#define glVertexAttribPointer imgl3wProcs.gl.VertexAttribPointer
|
||||
#define glViewport imgl3wProcs.gl.Viewport
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef IMGL3W_IMPL
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define GL3W_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#if defined(_WIN32)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
static HMODULE libgl;
|
||||
typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR);
|
||||
static GL3WglGetProcAddr wgl_get_proc_address;
|
||||
|
||||
static int open_libgl(void)
|
||||
{
|
||||
libgl = LoadLibraryA("opengl32.dll");
|
||||
if (!libgl)
|
||||
return GL3W_ERROR_LIBRARY_OPEN;
|
||||
wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, "wglGetProcAddress");
|
||||
return GL3W_OK;
|
||||
}
|
||||
|
||||
static void close_libgl(void) { FreeLibrary(libgl); }
|
||||
static GL3WglProc get_proc(const char *proc)
|
||||
{
|
||||
GL3WglProc res;
|
||||
res = (GL3WglProc)wgl_get_proc_address(proc);
|
||||
if (!res)
|
||||
res = (GL3WglProc)GetProcAddress(libgl, proc);
|
||||
return res;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void *libgl;
|
||||
static int open_libgl(void)
|
||||
{
|
||||
libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!libgl)
|
||||
return GL3W_ERROR_LIBRARY_OPEN;
|
||||
return GL3W_OK;
|
||||
}
|
||||
|
||||
static void close_libgl(void) { dlclose(libgl); }
|
||||
|
||||
static GL3WglProc get_proc(const char *proc)
|
||||
{
|
||||
GL3WglProc res;
|
||||
*(void **)(&res) = dlsym(libgl, proc);
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void* libgl; // OpenGL library
|
||||
static void* libglx; // GLX library
|
||||
static void* libegl; // EGL library
|
||||
static GL3WGetProcAddressProc gl_get_proc_address;
|
||||
|
||||
static void close_libgl(void)
|
||||
{
|
||||
if (libgl) {
|
||||
dlclose(libgl);
|
||||
libgl = NULL;
|
||||
}
|
||||
if (libegl) {
|
||||
dlclose(libegl);
|
||||
libegl = NULL;
|
||||
}
|
||||
if (libglx) {
|
||||
dlclose(libglx);
|
||||
libglx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_library_loaded(const char* name, void** lib)
|
||||
{
|
||||
*lib = dlopen(name, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
|
||||
return *lib != NULL;
|
||||
}
|
||||
|
||||
static int open_libs(void)
|
||||
{
|
||||
// On Linux we have two APIs to get process addresses: EGL and GLX.
|
||||
// EGL is supported under both X11 and Wayland, whereas GLX is X11-specific.
|
||||
|
||||
libgl = NULL;
|
||||
libegl = NULL;
|
||||
libglx = NULL;
|
||||
|
||||
// First check what's already loaded, the windowing library might have
|
||||
// already loaded either EGL or GLX and we want to use the same one.
|
||||
|
||||
if (is_library_loaded("libEGL.so.1", &libegl) ||
|
||||
is_library_loaded("libGLX.so.0", &libglx)) {
|
||||
libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (libgl)
|
||||
return GL3W_OK;
|
||||
else
|
||||
close_libgl();
|
||||
}
|
||||
|
||||
if (is_library_loaded("libGL.so", &libgl))
|
||||
return GL3W_OK;
|
||||
if (is_library_loaded("libGL.so.1", &libgl))
|
||||
return GL3W_OK;
|
||||
if (is_library_loaded("libGL.so.3", &libgl))
|
||||
return GL3W_OK;
|
||||
|
||||
// Neither is already loaded, so we have to load one. Try EGL first
|
||||
// because it is supported under both X11 and Wayland.
|
||||
|
||||
// Load OpenGL + EGL
|
||||
libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL);
|
||||
libegl = dlopen("libEGL.so.1", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (libgl && libegl)
|
||||
return GL3W_OK;
|
||||
else
|
||||
close_libgl();
|
||||
|
||||
// Fall back to legacy libGL, which includes GLX
|
||||
// While most systems use libGL.so.1, NetBSD seems to use that libGL.so.3. See https://github.com/ocornut/imgui/issues/6983
|
||||
libgl = dlopen("libGL.so", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!libgl)
|
||||
libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!libgl)
|
||||
libgl = dlopen("libGL.so.3", RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
if (libgl)
|
||||
return GL3W_OK;
|
||||
|
||||
return GL3W_ERROR_LIBRARY_OPEN;
|
||||
}
|
||||
|
||||
static int open_libgl(void)
|
||||
{
|
||||
int res = open_libs();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (libegl)
|
||||
*(void**)(&gl_get_proc_address) = dlsym(libegl, "eglGetProcAddress");
|
||||
else if (libglx)
|
||||
*(void**)(&gl_get_proc_address) = dlsym(libglx, "glXGetProcAddressARB");
|
||||
else
|
||||
*(void**)(&gl_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB");
|
||||
|
||||
if (!gl_get_proc_address) {
|
||||
close_libgl();
|
||||
return GL3W_ERROR_LIBRARY_OPEN;
|
||||
}
|
||||
|
||||
return GL3W_OK;
|
||||
}
|
||||
|
||||
static GL3WglProc get_proc(const char* proc)
|
||||
{
|
||||
GL3WglProc res = NULL;
|
||||
|
||||
// Before EGL version 1.5, eglGetProcAddress doesn't support querying core
|
||||
// functions and may return a dummy function if we try, so try to load the
|
||||
// function from the GL library directly first.
|
||||
if (libegl)
|
||||
*(void**)(&res) = dlsym(libgl, proc);
|
||||
|
||||
if (!res)
|
||||
res = gl_get_proc_address(proc);
|
||||
|
||||
if (!libegl && !res)
|
||||
*(void**)(&res) = dlsym(libgl, proc);
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct { int major, minor; } version;
|
||||
|
||||
static int parse_version(void)
|
||||
{
|
||||
if (!glGetIntegerv)
|
||||
return GL3W_ERROR_INIT;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &version.major);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &version.minor);
|
||||
if (version.major == 0 && version.minor == 0)
|
||||
{
|
||||
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
|
||||
if (const char* gl_version = (const char*)glGetString(GL_VERSION))
|
||||
sscanf(gl_version, "%d.%d", &version.major, &version.minor);
|
||||
}
|
||||
if (version.major < 2)
|
||||
return GL3W_ERROR_OPENGL_VERSION;
|
||||
return GL3W_OK;
|
||||
}
|
||||
|
||||
static void load_procs(GL3WGetProcAddressProc proc);
|
||||
|
||||
int imgl3wInit(void)
|
||||
{
|
||||
int res = open_libgl();
|
||||
if (res)
|
||||
return res;
|
||||
atexit(close_libgl);
|
||||
return imgl3wInit2(get_proc);
|
||||
}
|
||||
|
||||
int imgl3wInit2(GL3WGetProcAddressProc proc)
|
||||
{
|
||||
load_procs(proc);
|
||||
return parse_version();
|
||||
}
|
||||
|
||||
int imgl3wIsSupported(int major, int minor)
|
||||
{
|
||||
if (major < 2)
|
||||
return 0;
|
||||
if (version.major == major)
|
||||
return version.minor >= minor;
|
||||
return version.major >= major;
|
||||
}
|
||||
|
||||
GL3WglProc imgl3wGetProcAddress(const char *proc) { return get_proc(proc); }
|
||||
|
||||
static const char *proc_names[] = {
|
||||
"glActiveTexture",
|
||||
"glAttachShader",
|
||||
"glBindBuffer",
|
||||
"glBindSampler",
|
||||
"glBindTexture",
|
||||
"glBindVertexArray",
|
||||
"glBlendEquation",
|
||||
"glBlendEquationSeparate",
|
||||
"glBlendFuncSeparate",
|
||||
"glBufferData",
|
||||
"glBufferSubData",
|
||||
"glClear",
|
||||
"glClearColor",
|
||||
"glCompileShader",
|
||||
"glCreateProgram",
|
||||
"glCreateShader",
|
||||
"glDeleteBuffers",
|
||||
"glDeleteProgram",
|
||||
"glDeleteShader",
|
||||
"glDeleteTextures",
|
||||
"glDeleteVertexArrays",
|
||||
"glDetachShader",
|
||||
"glDisable",
|
||||
"glDisableVertexAttribArray",
|
||||
"glDrawElements",
|
||||
"glDrawElementsBaseVertex",
|
||||
"glEnable",
|
||||
"glEnableVertexAttribArray",
|
||||
"glFlush",
|
||||
"glGenBuffers",
|
||||
"glGenTextures",
|
||||
"glGenVertexArrays",
|
||||
"glGetAttribLocation",
|
||||
"glGetError",
|
||||
"glGetIntegerv",
|
||||
"glGetProgramInfoLog",
|
||||
"glGetProgramiv",
|
||||
"glGetShaderInfoLog",
|
||||
"glGetShaderiv",
|
||||
"glGetString",
|
||||
"glGetStringi",
|
||||
"glGetUniformLocation",
|
||||
"glGetVertexAttribPointerv",
|
||||
"glGetVertexAttribiv",
|
||||
"glIsEnabled",
|
||||
"glIsProgram",
|
||||
"glLinkProgram",
|
||||
"glPixelStorei",
|
||||
"glPolygonMode",
|
||||
"glReadPixels",
|
||||
"glScissor",
|
||||
"glShaderSource",
|
||||
"glTexImage2D",
|
||||
"glTexParameteri",
|
||||
"glUniform1i",
|
||||
"glUniformMatrix4fv",
|
||||
"glUseProgram",
|
||||
"glVertexAttribPointer",
|
||||
"glViewport",
|
||||
};
|
||||
|
||||
GL3W_API union ImGL3WProcs imgl3wProcs;
|
||||
|
||||
static void load_procs(GL3WGetProcAddressProc proc)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < GL3W_ARRAY_SIZE(proc_names); i++)
|
||||
imgl3wProcs.ptr[i] = proc(proc_names[i]);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
1232
imgui_impl_sdl2.cpp
Normal file
1232
imgui_impl_sdl2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
50
imgui_impl_sdl2.h
Normal file
50
imgui_impl_sdl2.h
Normal file
@ -0,0 +1,50 @@
|
||||
// dear imgui: Platform Backend for SDL2
|
||||
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Gamepad support.
|
||||
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
|
||||
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||
// Missing features or Issues:
|
||||
// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows).
|
||||
// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct SDL_Window;
|
||||
struct SDL_Renderer;
|
||||
struct _SDL_GameController;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOther(SDL_Window* window);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame();
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
|
||||
|
||||
// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this.
|
||||
// When using manual mode, caller is responsible for opening/closing gamepad.
|
||||
enum ImGui_ImplSDL2_GamepadMode { ImGui_ImplSDL2_GamepadMode_AutoFirst, ImGui_ImplSDL2_GamepadMode_AutoAll, ImGui_ImplSDL2_GamepadMode_Manual };
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array = nullptr, int manual_gamepads_count = -1);
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
1183
imgui_impl_sdl3.cpp
Normal file
1183
imgui_impl_sdl3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
50
imgui_impl_sdl3.h
Normal file
50
imgui_impl_sdl3.h
Normal file
@ -0,0 +1,50 @@
|
||||
// dear imgui: Platform Backend for SDL3
|
||||
// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..)
|
||||
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Gamepad support.
|
||||
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [x] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable' -> the OS animation effect when window gets created/destroyed is problematic. SDL2 backend doesn't have issue.
|
||||
// Missing features or Issues:
|
||||
// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows).
|
||||
// [x] Platform: IME support. Position somehow broken in SDL3 + app needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct SDL_Window;
|
||||
struct SDL_Renderer;
|
||||
struct SDL_Gamepad;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOther(SDL_Window* window);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL3_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL3_NewFrame();
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event);
|
||||
|
||||
// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this.
|
||||
// When using manual mode, caller is responsible for opening/closing gamepad.
|
||||
enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual };
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1);
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
3941
imgui_internal.h
Normal file
3941
imgui_internal.h
Normal file
File diff suppressed because it is too large
Load Diff
4525
imgui_tables.cpp
Normal file
4525
imgui_tables.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1184
imgui_tex_inspect.cpp
Normal file
1184
imgui_tex_inspect.cpp
Normal file
File diff suppressed because it is too large
Load Diff
308
imgui_tex_inspect.h
Normal file
308
imgui_tex_inspect.h
Normal file
@ -0,0 +1,308 @@
|
||||
// ImGuiTexInspect, a texture inspector widget for dear imgui
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h"
|
||||
|
||||
namespace ImGuiTexInspect
|
||||
{
|
||||
struct Context;
|
||||
struct Transform2D;
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] INIT & SHUTDOWN
|
||||
//-------------------------------------------------------------------------
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
Context *CreateContext();
|
||||
void DestroyContext(Context *);
|
||||
void SetCurrentContext(Context *);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] BASIC USAGE
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
enum InspectorAlphaMode
|
||||
{
|
||||
InspectorAlphaMode_ImGui, // Alpha is transparency so you see the ImGui panel background behind image
|
||||
InspectorAlphaMode_Black, // Alpha is used to blend over a black background
|
||||
InspectorAlphaMode_White, // Alpha is used to blend over a white background
|
||||
InspectorAlphaMode_CustomColor // Alpha is used to blend over a custom colour.
|
||||
};
|
||||
|
||||
typedef ImU64 InspectorFlags;
|
||||
enum InspectorFlags_
|
||||
{
|
||||
InspectorFlags_ShowWrap = 1 << 0, // Draw beyong the [0,1] uv range. What you see will depend on API
|
||||
InspectorFlags_NoForceFilterNearest = 1 << 1, // Normally we force nearest neighbour sampling when zoomed in. Set to disable this.
|
||||
InspectorFlags_NoGrid = 1 << 2, // By default a grid is shown at high zoom levels
|
||||
InspectorFlags_NoTooltip = 1 << 3, // Disable tooltip on hover
|
||||
InspectorFlags_FillHorizontal = 1 << 4, // Scale to fill available space horizontally
|
||||
InspectorFlags_FillVertical = 1 << 5, // Scale to fill available space vertically
|
||||
InspectorFlags_NoAutoReadTexture = 1 << 6, // By default texture data is read to CPU every frame for tooltip and annotations
|
||||
InspectorFlags_FlipX = 1 << 7, // Horizontally flip the way the texture is displayed
|
||||
InspectorFlags_FlipY = 1 << 8, // Vertically flip the way the texture is displayed
|
||||
};
|
||||
|
||||
/* Use one of these Size structs if you want to specify an exact size for the inspector panel.
|
||||
* E.g.
|
||||
* BeginInspectorPanel("MyPanel", texture_1K, ImVec2(1024,1024), 0, SizeExcludingBorder(ImVec2(1024,1024)));
|
||||
*
|
||||
* However, most of the time the default size will be fine. E.g.
|
||||
*
|
||||
* BeginInspectorPanel("MyPanel", texture_1K, ImVec2(1024,1024));
|
||||
*/
|
||||
struct SizeIncludingBorder {ImVec2 Size; SizeIncludingBorder(ImVec2 size):Size(size){}};
|
||||
struct SizeExcludingBorder {ImVec2 size; SizeExcludingBorder(ImVec2 size):size(size){}};
|
||||
/* BeginInspectorPanel
|
||||
* Returns true if panel is drawn. Note that flags will only be considered on the first call */
|
||||
bool BeginInspectorPanel(const char *name, ImTextureID, ImVec2 textureSize, InspectorFlags flags = 0);
|
||||
bool BeginInspectorPanel(const char *name, ImTextureID, ImVec2 textureSize, InspectorFlags flags, SizeIncludingBorder size);
|
||||
bool BeginInspectorPanel(const char *name, ImTextureID, ImVec2 textureSize, InspectorFlags flags, SizeExcludingBorder size);
|
||||
|
||||
/* EndInspectorPanel
|
||||
* Always call after BeginInspectorPanel and after you have drawn any required annotations*/
|
||||
void EndInspectorPanel();
|
||||
|
||||
/* ReleaseInspectorData
|
||||
* ImGuiTexInspect keeps texture data cached in memory. If you know you won't
|
||||
* be displaying a particular panel for a while you can call this to release
|
||||
* the memory. It won't be allocated again until next time you call
|
||||
* BeginInspectorPanel. If id is NULL then the current (most recent) inspector
|
||||
* will be affected. Unless you have a lot of different Inspector instances
|
||||
* you can probably not worry about this. Call CurrentInspector_GetID to get
|
||||
* the ID of an inspector.
|
||||
*/
|
||||
void ReleaseInspectorData(ImGuiID id);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] CURRENT INSPECTOR MANIPULATORS
|
||||
//-------------------------------------------------------------------------
|
||||
/* All the functions starting with CurrentInspector_ can be used after calling
|
||||
* BeginInspector until the end of the frame. It is not necessary to call them
|
||||
* before the matching EndInspectorPanel
|
||||
*/
|
||||
|
||||
/* CurrentInspector_SetColorMatrix
|
||||
* colorMatrix and colorOffset describe the transform which happens to the
|
||||
* color of each texel.
|
||||
* The calculation is finalColor = colorMatrix * originalColor + colorOffset.
|
||||
* Where finalColor, originalColor and colorOffset are column vectors with
|
||||
* components (r,g,b,a) and colorMatrix is a column-major matrix.
|
||||
*/
|
||||
void CurrentInspector_SetColorMatrix(const float (&colorMatrix)[16], const float (&colorOffset)[4]);
|
||||
void CurrentInspector_ResetColorMatrix();
|
||||
|
||||
/* CurrentInspector_SetAlphaMode - see enum comments for details*/
|
||||
void CurrentInspector_SetAlphaMode(InspectorAlphaMode);
|
||||
void CurrentInspector_SetFlags(InspectorFlags toSet, InspectorFlags toClear = 0);
|
||||
inline void CurrentInspector_ClearFlags(InspectorFlags toClear) {CurrentInspector_SetFlags(0, toClear);}
|
||||
void CurrentInspector_SetGridColor(ImU32 color);
|
||||
void CurrentInspector_SetMaxAnnotations(int maxAnnotations);
|
||||
|
||||
/* CurrentInspector_InvalidateTextureCache
|
||||
* If using the InspectorFlags_NoAutoReadTexture flag then call this to
|
||||
* indicate your texture has changed context.
|
||||
*/
|
||||
void CurrentInspector_InvalidateTextureCache();
|
||||
|
||||
/* CurrentInspector_SetCustomBackgroundColor
|
||||
* If using InspectorAlphaMode_CustomColor then this is the color that will be
|
||||
* blended as the background where alpha is less than one.
|
||||
*/
|
||||
void CurrentInspector_SetCustomBackgroundColor(ImVec4 color);
|
||||
void CurrentInspector_SetCustomBackgroundColor(ImU32 color);
|
||||
|
||||
/* CurrentInspector_GetID
|
||||
* Get the ID of the current inspector. Currently only used for calling
|
||||
* ReleaseInspectorData.
|
||||
*/
|
||||
ImGuiID CurrentInspector_GetID();
|
||||
|
||||
/* Some convenience functions for drawing ImGui controls for the current Inspector */
|
||||
void DrawColorMatrixEditor(); // ColorMatrix editor. See comments on ColorMatrix below.
|
||||
void DrawGridEditor(); // Grid editor. Enable/Disable grid. Set Grid Color.
|
||||
void DrawColorChannelSelector(); // For toggling R,G,B channels
|
||||
void DrawAlphaModeSelector(); // A combo box for selecting the alpha mode
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] CONTEXT-WIDE SETTINGS
|
||||
//-------------------------------------------------------------------------
|
||||
/* SetZoomRate
|
||||
* factor should be greater than 1. A value of 1.5 means one mouse wheel
|
||||
* scroll will increase zoom level by 50%. The factor used for zooming out is
|
||||
* 1/factor. */
|
||||
void SetZoomRate(float factor);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] ANNOTATION TOOLS
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/* DrawAnnotationLine
|
||||
* Convenience function to add a line to draw list using texel coordinates.
|
||||
*/
|
||||
void DrawAnnotationLine(ImDrawList *drawList, ImVec2 fromTexel, ImVec2 toTexel, Transform2D texelsToPixels, ImU32 color);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Annotation Classes
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/* To draw annotations call DrawAnnotions in between BeginInspectorPanel and
|
||||
* EndInspectorPanel. Example usage:
|
||||
* DrawAnnotations(ValueText(ValueText::HexString));
|
||||
*
|
||||
* To provide your own Annotation drawing class just define a class that
|
||||
* implements the DrawAnnotation method. See imgui_tex_inspect_demo.cpp
|
||||
* for an example.
|
||||
*/
|
||||
template <typename T>
|
||||
void DrawAnnotations(T drawer, ImU64 maxAnnotatedTexels = 0);
|
||||
|
||||
/* ValueText
|
||||
* An annoation class that draws text inside each texel when zoom level is high enough for it to fit.
|
||||
* The text shows the value of the texel. E.g. "R:255, G: 128, B:0, A:255"
|
||||
*/
|
||||
class ValueText
|
||||
{
|
||||
protected:
|
||||
int TextRowCount;
|
||||
int TextColumnCount;
|
||||
const char *TextFormatString;
|
||||
bool FormatAsFloats;
|
||||
|
||||
public:
|
||||
enum Format
|
||||
{
|
||||
HexString, // E.g. #EF97B9FF
|
||||
BytesHex, // E.g. R:#EF G:#97 B:#B9 A:#FF (split over 4 lines)
|
||||
BytesDec, // E.g. R:239 G: 151 B:185 A:255 (split over 4 lines)
|
||||
Floats // E.g. 0.937 0.592 0.725 1.000 (split over 4 lines)
|
||||
};
|
||||
ValueText(Format format = HexString);
|
||||
void DrawAnnotation(ImDrawList *drawList, ImVec2 texel, Transform2D texelsToPixels, ImVec4 value);
|
||||
};
|
||||
|
||||
/* Arrow
|
||||
* An annotation class that draws an arrow inside each texel when zoom level is
|
||||
* high enough. The direction and length of the arrow are determined by texel
|
||||
* values.
|
||||
* The X and Y components of the arrow is determined by the VectorIndex_x, and
|
||||
* VectorIndex_y channels of the texel value. Examples:
|
||||
|
||||
* VectorIndex_x = 0, VectorIndex_y = 1 means X component is red and Y component is green
|
||||
* VectorIndex_x = 1, VectorIndex_y = 2 means X component is green and Y component is blue
|
||||
* VectorIndex_x = 0, VectorIndex_y = 3 means X component is red and Y component is alpha
|
||||
*
|
||||
* ZeroPoint is the texel value which corresponds to a zero length vector. E.g.
|
||||
* ZeroPoint = (0.5, 0.5) means (0.5, 0.5) will be drawn as a zero length arrow
|
||||
*
|
||||
* All public properties can be directly manipulated. There are also presets that can be set
|
||||
* by calling UsePreset.
|
||||
|
||||
*/
|
||||
class Arrow
|
||||
{
|
||||
public:
|
||||
int VectorIndex_x;
|
||||
int VectorIndex_y;
|
||||
ImVec2 LineScale;
|
||||
ImVec2 ZeroPoint = {0, 0};
|
||||
|
||||
enum Preset
|
||||
{
|
||||
NormalMap, // For normal maps. I.e. Arrow is in (R,G) channels. 128, 128 is zero point
|
||||
NormalizedFloat // Arrow in (R,G) channels. 0,0 is zero point, (1,0) will draw an arrow exactly to
|
||||
// right edge of texture. (0,-1) will draw exactly to the bottom etc.
|
||||
};
|
||||
Arrow(int xVectorIndex = 0, int yVectorIndex = 1, ImVec2 lineScale = ImVec2(1, 1));
|
||||
Arrow &UsePreset(Preset);
|
||||
void DrawAnnotation(ImDrawList *drawList, ImVec2 texel, Transform2D texelsToPixels, ImVec4 value);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] INTERNAL
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
struct Transform2D
|
||||
{
|
||||
ImVec2 Scale;
|
||||
ImVec2 Translate;
|
||||
|
||||
/* Transform a vector by this transform. Scale is applied first */
|
||||
ImVec2 operator*(const ImVec2 &rhs) const
|
||||
{
|
||||
return ImVec2(Scale.x * rhs.x + Translate.x, Scale.y * rhs.y + Translate.y);
|
||||
}
|
||||
|
||||
/* Return an inverse transform such that transform.Inverse() * transform * vector == vector*/
|
||||
Transform2D Inverse() const
|
||||
{
|
||||
ImVec2 inverseScale(1 / Scale.x, 1 / Scale.y);
|
||||
return {inverseScale, ImVec2(-inverseScale.x * Translate.x, -inverseScale.y * Translate.y)};
|
||||
}
|
||||
};
|
||||
|
||||
struct BufferDesc
|
||||
{
|
||||
float *Data_float = nullptr; // Only one of these
|
||||
ImU8 *Data_uint8_t = nullptr; // two pointers should be non NULL
|
||||
size_t BufferByteSize = 0; // Size of buffer pointed to by one of above pointers
|
||||
int Stride = 0; // Measured in size of data type, not bytes!
|
||||
int LineStride = 0; // Measured in size of data type, not bytes!
|
||||
int StartX = 0; // Texel coordinates of data start
|
||||
int StartY = 0;
|
||||
int Width = 0; // Size of block of texels which are in data
|
||||
int Height = 0;
|
||||
|
||||
unsigned char ChannelCount = 0; // Number of color channels in data. E.g. 2 means just red and green
|
||||
|
||||
/* These 4 values describe where each color is stored relative to the beginning of the texel in memory
|
||||
* E.g. the float containing the red value would be at:
|
||||
* Data_float[texelIndex + bufferDesc.Red]
|
||||
*/
|
||||
unsigned char Red = 0;
|
||||
unsigned char Green = 0;
|
||||
unsigned char Blue = 0;
|
||||
unsigned char Alpha = 0;
|
||||
};
|
||||
|
||||
/* We use this struct for annotations rather than the Inspector struct so that
|
||||
* the whole Inspector struct doesn't have to be exposed in this header.
|
||||
*/
|
||||
struct AnnotationsDesc
|
||||
{
|
||||
ImDrawList *DrawList;
|
||||
ImVec2 TexelViewSize; // How many texels are visible for annotating
|
||||
ImVec2 TexelTopLeft; // Coordinated in texture space of top left visible texel
|
||||
BufferDesc Buffer; // Description of cache texel data
|
||||
Transform2D TexelsToPixels; // Transform to go from texel space to screen pixel space
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] FORWARD DECLARATIONS FOR TEMPLATE IMPLEMENTATION - Do not call directly
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
ImVec4 GetTexel(const BufferDesc *bd, int x, int y);
|
||||
bool GetAnnotationDesc(AnnotationsDesc *, ImU64 maxAnnotatedTexels);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] TEMPLATE IMPLEMENTATION
|
||||
//-------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
void DrawAnnotations(T drawer, ImU64 maxAnnotatedTexels)
|
||||
{
|
||||
AnnotationsDesc ad;
|
||||
if (GetAnnotationDesc(&ad, maxAnnotatedTexels))
|
||||
{
|
||||
ImVec2 texelBottomRight = ImVec2(ad.TexelTopLeft.x + ad.TexelViewSize.x, ad.TexelTopLeft.y + ad.TexelViewSize.y);
|
||||
for (int ty = (int)ad.TexelTopLeft.y; ty < (int)texelBottomRight.y; ++ty)
|
||||
{
|
||||
for (int tx = (int)ad.TexelTopLeft.x; tx < (int)texelBottomRight.x; ++tx)
|
||||
{
|
||||
ImVec4 color = GetTexel(&ad.Buffer, tx, ty);
|
||||
ImVec2 center = {(float)tx + 0.5f, (float)ty + 0.5f};
|
||||
drawer.DrawAnnotation(ad.DrawList, center, ad.TexelsToPixels, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ImGuiTexInspect
|
181
imgui_tex_inspect_internal.h
Normal file
181
imgui_tex_inspect_internal.h
Normal file
@ -0,0 +1,181 @@
|
||||
// ImGuiTexInspect, a texture inspector widget for dear imgui
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_tex_inspect.h"
|
||||
|
||||
namespace ImGuiTexInspect
|
||||
{
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] UTILITIES
|
||||
//-------------------------------------------------------------------------
|
||||
// Returns true if a flag is set
|
||||
template <typename TSet, typename TFlag>
|
||||
static inline bool HasFlag(TSet set, TFlag flag)
|
||||
{
|
||||
return (set & flag) == flag;
|
||||
}
|
||||
|
||||
// Set flag or flags in set
|
||||
template <typename TSet, typename TFlag>
|
||||
static inline void SetFlag(TSet &set, TFlag flags)
|
||||
{
|
||||
set = static_cast<TSet>(set | flags);
|
||||
}
|
||||
|
||||
// Clear flag or flags in set
|
||||
template <typename TSet, typename TFlag>
|
||||
static inline void ClearFlag(TSet &set, TFlag flag)
|
||||
{
|
||||
set = static_cast<TSet>(set & ~flag);
|
||||
}
|
||||
|
||||
// Proper modulus operator, as opposed to remainder as calculated by %
|
||||
template <typename T>
|
||||
static inline T Modulus(T a, T b)
|
||||
{
|
||||
return a - b * ImFloor(a / b);
|
||||
}
|
||||
|
||||
// Defined in recent versions of imgui_internal.h. Included here in case user is on older
|
||||
// imgui version.
|
||||
static inline float ImFloor(float f)
|
||||
{
|
||||
return (float)((f >= 0 || (int)f == f) ? (int)f : (int)f - 1);
|
||||
}
|
||||
|
||||
static inline float Round(float f)
|
||||
{
|
||||
return ImFloor(f + 0.5f);
|
||||
}
|
||||
|
||||
static inline ImVec2 Abs(ImVec2 v)
|
||||
{
|
||||
return ImVec2(ImAbs(v.x), ImAbs(v.y));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] STRUCTURES
|
||||
//-------------------------------------------------------------------------
|
||||
struct ShaderOptions
|
||||
{
|
||||
float ColorTransform[16] = {}; // See CurrentInspector_SetColorMatrix for details
|
||||
float ColorOffset[4] = {};
|
||||
|
||||
ImVec4 BackgroundColor = {0,0,0,0}; // Color used for alpha blending
|
||||
float PremultiplyAlpha = 0; // If 1 then color will be multiplied by alpha in shader, before blend stage
|
||||
float DisableFinalAlpha = 0; // If 1 then fragment shader will always output alpha = 1
|
||||
|
||||
bool ForceNearestSampling = false; // If true fragment shader will always sample from texel centers
|
||||
|
||||
ImVec2 GridWidth = {0,0}; // Width in UV coords of grid line
|
||||
ImVec4 GridColor = {0,0,0,0};
|
||||
|
||||
void ResetColorTransform();
|
||||
ShaderOptions();
|
||||
};
|
||||
|
||||
struct Inspector
|
||||
{
|
||||
ImGuiID ID;
|
||||
bool Initialized = false;
|
||||
|
||||
// Texture
|
||||
ImTextureID Texture = ImTextureID{};
|
||||
ImVec2 TextureSize = {0, 0}; // Size in texels of texture
|
||||
float PixelAspectRatio = 1; // Values other than 1 not supported yet
|
||||
|
||||
// View State
|
||||
bool IsDragging = false; // Is user currently dragging to pan view
|
||||
ImVec2 PanPos = {0.5f, 0.5f}; // The UV value at the center of the current view
|
||||
ImVec2 Scale = {1, 1}; // 1 pixel is 1 texel
|
||||
|
||||
ImVec2 PanelTopLeftPixel = {0, 0}; // Top left of view in ImGui pixel coordinates
|
||||
ImVec2 PanelSize = {0, 0}; // Size of area allocated to drawing the image in pixels.
|
||||
|
||||
ImVec2 ViewTopLeftPixel = {0, 0}; // Position in ImGui pixel coordinates
|
||||
ImVec2 ViewSize = {0, 0}; // Rendered size of current image. This could be smaller than panel size if user has zoomed out.
|
||||
ImVec2 ViewSizeUV = {0, 0}; // Visible region of the texture in UV coordinates
|
||||
|
||||
/* Conversion transforms to go back and forth between screen pixels (what ImGui considers screen pixels) and texels*/
|
||||
Transform2D TexelsToPixels;
|
||||
Transform2D PixelsToTexels;
|
||||
|
||||
// Cached pixel data
|
||||
bool HaveCurrentTexelData = false;
|
||||
BufferDesc Buffer;
|
||||
|
||||
/* We don't actually access texel data through this pointer. We just
|
||||
* manage its lifetime. The backend might have asked us to allocated a
|
||||
* buffer, or it might not. The pointer we actually use to access texel
|
||||
* data is in the Buffer object above (which depending on what the backend
|
||||
* did might point to the same memory as this pointer)
|
||||
*/
|
||||
ImU8 *DataBuffer = nullptr;
|
||||
size_t DataBufferSize = 0;
|
||||
|
||||
// Configuration
|
||||
InspectorFlags Flags = 0;
|
||||
|
||||
// Background mode
|
||||
InspectorAlphaMode AlphaMode = InspectorAlphaMode_ImGui;
|
||||
ImVec4 CustomBackgroundColor = {0, 0, 0, 1};
|
||||
|
||||
// Scaling limits
|
||||
ImVec2 ScaleMin = {0.02f, 0.02f};
|
||||
ImVec2 ScaleMax = {500, 500};
|
||||
|
||||
// Grid
|
||||
float MinimumGridSize = 4; // Don't draw the grid if lines would be closer than MinimumGridSize pixels
|
||||
|
||||
// Annotations
|
||||
ImU32 MaxAnnotatedTexels = 0;
|
||||
|
||||
// Color transformation
|
||||
ShaderOptions ActiveShaderOptions;
|
||||
ShaderOptions CachedShaderOptions;
|
||||
|
||||
~Inspector();
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] INTERNAL FUNCTIONS
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
Inspector *GetByKey(const Context *ctx, ImGuiID key);
|
||||
Inspector *GetOrAddByKey(Context *ctx, ImGuiID key);
|
||||
|
||||
void SetPanPos(Inspector *inspector, ImVec2 pos);
|
||||
void SetScale(Inspector *inspector, ImVec2 scale);
|
||||
void SetScale(Inspector *inspector, float scaleY);
|
||||
void RoundPanPos(Inspector *inspector);
|
||||
|
||||
ImU8 *GetBuffer(Inspector *inspector, size_t bytes);
|
||||
|
||||
/* GetTexelsToPixels
|
||||
* Calculate a transform to convert from texel coordinates to screen pixel coordinates
|
||||
* */
|
||||
Transform2D GetTexelsToPixels(ImVec2 screenTopLeft, ImVec2 screenViewSize, ImVec2 uvTopLeft, ImVec2 uvViewSize, ImVec2 textureSize);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] IMGUI UTILS
|
||||
//-------------------------------------------------------------------------
|
||||
/* TextVector
|
||||
* Draws a single-column ImGui table with one row for each provided string
|
||||
*/
|
||||
void TextVector(const char *title, const char *const *strings, int n);
|
||||
|
||||
/* PushDisabled & PopDisabled
|
||||
* Push and Pop and ImGui styles that disable and "grey out" ImGui elements
|
||||
* by making them non interactive and transparent*/
|
||||
void PushDisabled();
|
||||
void PopDisabled();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] BACKEND FUNCTIONS
|
||||
//-------------------------------------------------------------------------
|
||||
void BackEnd_SetShader(const ImDrawList *drawList, const ImDrawCmd *cmd, const Inspector *inspector);
|
||||
bool BackEnd_GetData(Inspector *inspector, ImTextureID texture, int x, int y, int width, int height, BufferDesc *buffer);
|
||||
|
||||
} // namespace ImGuiTexInspect
|
10577
imgui_widgets.cpp
Normal file
10577
imgui_widgets.cpp
Normal file
File diff suppressed because it is too large
Load Diff
627
imstb_rectpack.h
Normal file
627
imstb_rectpack.h
Normal file
@ -0,0 +1,627 @@
|
||||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
//
|
||||
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Before #including,
|
||||
//
|
||||
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||
//
|
||||
// in the file that you want to have the implementation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
typedef int stbrp_coord;
|
||||
|
||||
#define STBRP__MAXVAL 0x7fffffff
|
||||
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
context->extra[1].y = (1<<30);
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
//STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
1469
imstb_textedit.h
Normal file
1469
imstb_textedit.h
Normal file
File diff suppressed because it is too large
Load Diff
5085
imstb_truetype.h
Normal file
5085
imstb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
58
shaders/clarity.frag
Normal file
58
shaders/clarity.frag
Normal file
@ -0,0 +1,58 @@
|
||||
#version 330 core
|
||||
|
||||
float luminance(vec3 color) {
|
||||
return dot(color, vec3(0.2126, 0.7152, 0.0722));
|
||||
}
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float clarityValue; // -100 (blur) to 100 (sharpen)
|
||||
|
||||
// Simple Box Blur (approximates Gaussian for unsharp mask)
|
||||
vec3 boxBlur(sampler2D tex, vec2 uv, vec2 texelSize, int radius) {
|
||||
vec3 blurred = vec3(0.0);
|
||||
float weightSum = 0.0;
|
||||
float r = float(radius);
|
||||
|
||||
for (int x = -radius; x <= radius; ++x) {
|
||||
for (int y = -radius; y <= radius; ++y) {
|
||||
// Optional: Use Gaussian weights instead of box for better quality blur
|
||||
// float weight = exp(-(float(x*x + y*y)) / (2.0 * r*r));
|
||||
float weight = 1.0; // Box weight
|
||||
blurred += texture(tex, uv + vec2(x, y) * texelSize).rgb * weight;
|
||||
weightSum += weight;
|
||||
}
|
||||
}
|
||||
return blurred / weightSum;
|
||||
}
|
||||
|
||||
// Apply Clarity using Unsharp Masking
|
||||
vec3 applyClarity(vec3 originalColor, vec2 uv, float clarity) {
|
||||
if (abs(clarity) < 0.01) return originalColor; // No change
|
||||
|
||||
vec2 texelSize = 1.0 / textureSize(InputTexture, 0);
|
||||
// Clarity targets mid-frequencies, use a moderate blur radius
|
||||
int blurRadius = 2; // Adjust radius for desired frequency range (1-3 typical)
|
||||
|
||||
// 1. Create a blurred version (low-pass filter)
|
||||
vec3 blurredColor = boxBlur(InputTexture, uv, texelSize, blurRadius);
|
||||
|
||||
// 2. Calculate the high-pass detail (Original - Blurred)
|
||||
vec3 highPassDetail = originalColor - blurredColor;
|
||||
|
||||
// 3. Add the scaled detail back to the original
|
||||
// Map clarity -100..100 to a strength factor, e.g., 0..2
|
||||
float strength = clarity / 100.0; // Map to -1..1
|
||||
|
||||
vec3 clarifiedColor = originalColor + highPassDetail * strength;
|
||||
|
||||
return clarifiedColor;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(InputTexture, TexCoord);
|
||||
color.rgb = applyClarity(color.rgb, TexCoord, clarityValue);
|
||||
FragColor = vec4(max(color.rgb, vec3(0.0)), color.a);
|
||||
}
|
25
shaders/contrast.frag
Normal file
25
shaders/contrast.frag
Normal file
@ -0,0 +1,25 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float contrastValue; // Expecting value e.g., -100 to 100
|
||||
|
||||
// Perceptually accurate contrast adjustment
|
||||
vec3 applyContrast(vec3 color, float contrast) {
|
||||
float factor = pow(2.0, contrast / 50.0); // Exponential scaling for more natural feel
|
||||
|
||||
// Use 0.18 as middle gray (photographic standard)
|
||||
const vec3 midPoint = vec3(0.18);
|
||||
|
||||
// Apply contrast with proper pivot point
|
||||
return midPoint + factor * (color - midPoint);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(InputTexture, TexCoord);
|
||||
color.rgb = applyContrast(color.rgb, contrastValue);
|
||||
|
||||
// Clamp results to valid range
|
||||
FragColor = vec4(clamp(color.rgb, vec3(0.0), vec3(1.0)), color.a);
|
||||
}
|
198
shaders/dehaze.frag
Normal file
198
shaders/dehaze.frag
Normal file
@ -0,0 +1,198 @@
|
||||
#version 330 core
|
||||
/*
|
||||
* Advanced Atmospheric Dehazing Shader
|
||||
* ------------------------------------
|
||||
* This shader simulates the behavior of Adobe Lightroom/Photoshop's Dehaze feature using:
|
||||
*
|
||||
* 1. Dark Channel Prior (DCP) technique for estimating atmospheric light and transmission
|
||||
* - In photography, haze appears as a low-contrast, brightened, desaturated overlay
|
||||
* - DCP assumes that haze-free regions have at least one RGB channel with very low intensity
|
||||
* - By detecting regions where all RGB channels are high, we can identify hazy areas
|
||||
*
|
||||
* 2. Multi-scale contrast enhancement with perceptual considerations
|
||||
* - Detail recovery is based on local contrast improvement
|
||||
* - Preserves color relationships similar to Lightroom by boosting both contrast and saturation
|
||||
*
|
||||
* 3. Adaptive atmospheric light estimation
|
||||
* - Uses a simplified version of sky/atmospheric light detection
|
||||
* - Approximates the global atmospheric light color (typically grayish-blue)
|
||||
*
|
||||
* 4. Tone-aware processing
|
||||
* - Protects highlights from clipping, similar to Lightroom's implementation
|
||||
* - Preserves natural shadows while improving visibility
|
||||
*
|
||||
* 5. Implements a non-linear response curve that matches the perceptual effect of Lightroom's
|
||||
* dehaze slider within the -100 to +100 range
|
||||
*/
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float dehazeValue; // -100 to 100 (negative values add haze)
|
||||
|
||||
// Perceptual luminance weights (Rec. 709)
|
||||
const vec3 luminanceWeight = vec3(0.2126, 0.7152, 0.0722);
|
||||
|
||||
float luminance(vec3 color) {
|
||||
return dot(color, luminanceWeight);
|
||||
}
|
||||
|
||||
// Improved box blur with edge-aware weighting
|
||||
vec3 boxBlur(sampler2D tex, vec2 uv, vec2 texelSize, int radius) {
|
||||
vec3 blurred = vec3(0.0);
|
||||
float weightSum = 0.0;
|
||||
float r = float(radius);
|
||||
vec3 centerColor = texture(tex, uv).rgb;
|
||||
|
||||
for (int x = -radius; x <= radius; ++x) {
|
||||
for (int y = -radius; y <= radius; ++y) {
|
||||
vec2 offset = vec2(x, y) * texelSize;
|
||||
vec3 sampleColor = texture(tex, uv + offset).rgb;
|
||||
|
||||
// Edge-aware weight: lower weight for pixels that are very different
|
||||
float colorDist = distance(centerColor, sampleColor);
|
||||
float edgeWeight = exp(-colorDist * 10.0);
|
||||
|
||||
// Spatial weight: Gaussian
|
||||
float spatialWeight = exp(-(float(x*x + y*y)) / (2.0 * r*r));
|
||||
|
||||
float weight = spatialWeight * edgeWeight;
|
||||
blurred += sampleColor * weight;
|
||||
weightSum += weight;
|
||||
}
|
||||
}
|
||||
return blurred / max(weightSum, 0.0001);
|
||||
}
|
||||
|
||||
// Calculates dark channel (minimum of RGB channels) for a local region
|
||||
float getDarkChannel(sampler2D tex, vec2 uv, vec2 texelSize, int radius) {
|
||||
float darkChannel = 1.0;
|
||||
|
||||
for (int x = -radius; x <= radius; ++x) {
|
||||
for (int y = -radius; y <= radius; ++y) {
|
||||
vec2 offset = vec2(x, y) * texelSize;
|
||||
vec3 sampleColor = texture(tex, uv + offset).rgb;
|
||||
float minChannel = min(min(sampleColor.r, sampleColor.g), sampleColor.b);
|
||||
darkChannel = min(darkChannel, minChannel);
|
||||
}
|
||||
}
|
||||
|
||||
return darkChannel;
|
||||
}
|
||||
|
||||
// Estimates atmospheric light (typically the brightest pixel in hazy regions)
|
||||
vec3 estimateAtmosphericLight(sampler2D tex, vec2 uv, vec2 texelSize) {
|
||||
// Default slightly bluish-gray haze color, similar to most atmospheric conditions
|
||||
vec3 defaultAtmosphericLight = vec3(0.85, 0.9, 1.0);
|
||||
|
||||
// Find brightest area in a larger region
|
||||
vec3 brightest = vec3(0.0);
|
||||
float maxLum = 0.0;
|
||||
|
||||
int searchRadius = 10;
|
||||
for (int x = -searchRadius; x <= searchRadius; x += 2) {
|
||||
for (int y = -searchRadius; y <= searchRadius; y += 2) {
|
||||
vec2 offset = vec2(x, y) * texelSize;
|
||||
vec3 sampleColor = texture(tex, uv + offset).rgb;
|
||||
float lum = luminance(sampleColor);
|
||||
|
||||
if (lum > maxLum) {
|
||||
maxLum = lum;
|
||||
brightest = sampleColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blend between default and detected atmospheric light
|
||||
return mix(defaultAtmosphericLight, brightest, 0.7);
|
||||
}
|
||||
|
||||
// Tone protection function to preserve highlights and shadows
|
||||
vec3 protectTones(vec3 color, vec3 enhanced, float amount) {
|
||||
float lum = luminance(color);
|
||||
|
||||
// Highlight and shadow protection factors
|
||||
float highlightProtection = 1.0 - smoothstep(0.75, 0.98, lum);
|
||||
float shadowProtection = smoothstep(0.0, 0.2, lum);
|
||||
|
||||
float protection = mix(1.0, highlightProtection * shadowProtection, min(abs(amount) * 2.0, 1.0));
|
||||
|
||||
return mix(color, enhanced, protection);
|
||||
}
|
||||
|
||||
// Apply advanced dehazing algorithm
|
||||
vec3 applyDehaze(vec3 originalColor, vec2 uv, float dehaze) {
|
||||
// Convert dehaze from -100...100 to -1...1
|
||||
float amount = dehaze / 100.0;
|
||||
|
||||
if (abs(amount) < 0.01) return originalColor;
|
||||
|
||||
vec2 texelSize = 1.0 / textureSize(InputTexture, 0);
|
||||
|
||||
// --- Atmospheric Scattering Model: I = J × t + A × (1-t) ---
|
||||
// I is the observed hazy image, J is the dehazed image,
|
||||
// A is atmospheric light, t is transmission
|
||||
|
||||
// Calculate dark channel (key to estimating haze)
|
||||
int darkChannelRadius = 3;
|
||||
float darkChannel = getDarkChannel(InputTexture, uv, texelSize, darkChannelRadius);
|
||||
|
||||
// Estimate atmospheric light
|
||||
vec3 atmosphericLight = estimateAtmosphericLight(InputTexture, uv, texelSize);
|
||||
|
||||
// Calculate transmission map using dark channel prior
|
||||
float omega = 0.95; // Preserves a small amount of haze for realism
|
||||
float transmission = 1.0 - omega * darkChannel;
|
||||
|
||||
vec3 result;
|
||||
if (amount > 0.0) {
|
||||
// Remove haze (positive dehaze)
|
||||
float minTransmission = 0.1;
|
||||
float adjustedTransmission = max(transmission, minTransmission);
|
||||
adjustedTransmission = mix(1.0, adjustedTransmission, amount);
|
||||
|
||||
// Apply dehaze formula: J = (I - A) / t + A
|
||||
result = (originalColor - atmosphericLight) / adjustedTransmission + atmosphericLight;
|
||||
|
||||
// Additional local contrast enhancement
|
||||
vec3 localAverage = boxBlur(InputTexture, uv, texelSize, 3);
|
||||
vec3 localDetail = originalColor - localAverage;
|
||||
result = result + localDetail * (amount * 0.5);
|
||||
|
||||
// Saturation boost (typical of dehaze)
|
||||
float satBoost = 1.0 + amount * 0.3;
|
||||
float resultLum = luminance(result);
|
||||
result = mix(vec3(resultLum), result, satBoost);
|
||||
}
|
||||
else {
|
||||
// Add haze (negative dehaze)
|
||||
float hazeAmount = -amount;
|
||||
|
||||
// Blend with atmospheric light
|
||||
result = mix(originalColor, atmosphericLight, hazeAmount * 0.5);
|
||||
|
||||
// Reduce contrast to simulate haze
|
||||
vec3 localAverage = boxBlur(InputTexture, uv, texelSize, 5);
|
||||
result = mix(result, localAverage, hazeAmount * 0.3);
|
||||
|
||||
// Reduce saturation
|
||||
float resultLum = luminance(result);
|
||||
result = mix(result, vec3(resultLum), hazeAmount * 0.4);
|
||||
}
|
||||
|
||||
// Protect highlights and shadows
|
||||
result = protectTones(originalColor, result, amount);
|
||||
|
||||
// Non-linear response curve similar to Lightroom
|
||||
float blendFactor = 1.0 / (1.0 + exp(-abs(amount) * 3.0));
|
||||
result = mix(originalColor, result, blendFactor);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(InputTexture, TexCoord);
|
||||
color.rgb = applyDehaze(color.rgb, TexCoord, dehazeValue);
|
||||
FragColor = vec4(max(color.rgb, vec3(0.0)), color.a);
|
||||
}
|
44
shaders/exposure.frag
Normal file
44
shaders/exposure.frag
Normal file
@ -0,0 +1,44 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float exposureValue; // Expecting value in stops, e.g., -5.0 to 5.0
|
||||
|
||||
// Calculate perceptual luminance
|
||||
float luminance(vec3 color) {
|
||||
return dot(color, vec3(0.2126, 0.7152, 0.0722));
|
||||
}
|
||||
|
||||
// Apply exposure by adjusting luminance while preserving color relationships
|
||||
vec3 applyExposure(vec3 color, float exposureStops) {
|
||||
// Get original luminance
|
||||
float lum = luminance(color);
|
||||
|
||||
// Skip processing for very dark pixels to avoid division by zero
|
||||
if (lum < 0.0001) return color;
|
||||
|
||||
// Calculate exposure factor
|
||||
float exposureFactor = pow(2.0, exposureStops);
|
||||
|
||||
// Apply highlight compression when increasing exposure
|
||||
float newLum = lum * exposureFactor;
|
||||
if (exposureStops > 0.0 && newLum > 0.8) {
|
||||
// Soft highlight roll-off to prevent harsh clipping
|
||||
float excess = newLum - 0.8;
|
||||
newLum = 0.8 + 0.2 * (1.0 - exp(-excess * 5.0));
|
||||
}
|
||||
|
||||
// Scale RGB proportionally to maintain color relationships
|
||||
return color * (newLum / lum);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(InputTexture, TexCoord);
|
||||
|
||||
// Apply exposure adjustment
|
||||
color.rgb = applyExposure(color.rgb, exposureValue);
|
||||
|
||||
// Ensure output is in valid range
|
||||
FragColor = vec4(clamp(color.rgb, vec3(0.0), vec3(1.0)), color.a);
|
||||
}
|
66
shaders/highlights_shadows.frag
Normal file
66
shaders/highlights_shadows.frag
Normal file
@ -0,0 +1,66 @@
|
||||
#version 330 core
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float highlightsValue; // -100 to 100
|
||||
uniform float shadowsValue; // -100 to 100
|
||||
|
||||
// More accurate perceptual luminance (Rec. 709)
|
||||
float luminance(vec3 color) {
|
||||
return dot(color, vec3(0.2126, 0.7152, 0.0722));
|
||||
}
|
||||
|
||||
// Smoothstep with better performance than quintic
|
||||
float smootherStep(float edge0, float edge1, float x) {
|
||||
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
|
||||
return t * t * (3.0 - 2.0 * t);
|
||||
}
|
||||
|
||||
vec3 applyHighlightsShadows(vec3 color) {
|
||||
float lum = luminance(color);
|
||||
|
||||
// Define threshold values similar to Lightroom
|
||||
float shadowThreshold = 0.3;
|
||||
float highlightThreshold = 0.7;
|
||||
|
||||
// Calculate adjustment weights with smoother falloff
|
||||
float shadowWeight = 1.0 - smootherStep(0.0, shadowThreshold * 2.0, lum);
|
||||
float highlightWeight = smootherStep(highlightThreshold, 1.0, lum);
|
||||
|
||||
// Calculate adaptive adjustment factors
|
||||
float shadowFactor = shadowsValue > 0.0 ?
|
||||
mix(1.0, 1.0 + (shadowsValue / 100.0), shadowWeight) :
|
||||
mix(1.0, 1.0 + (shadowsValue / 150.0), shadowWeight);
|
||||
|
||||
float highlightFactor = highlightsValue > 0.0 ?
|
||||
mix(1.0, 1.0 - (highlightsValue / 150.0), highlightWeight) :
|
||||
mix(1.0, 1.0 - (highlightsValue / 100.0), highlightWeight);
|
||||
|
||||
// Apply adjustments while preserving colors
|
||||
vec3 adjusted = color * shadowFactor * highlightFactor;
|
||||
|
||||
// Preserve some saturation characteristics like Lightroom
|
||||
float newLum = luminance(adjusted);
|
||||
float saturationFactor = 1.0;
|
||||
|
||||
// Boost saturation slightly when lifting shadows (like Lightroom)
|
||||
// if (shadowsValue > 0.0 && lum < shadowThreshold) {
|
||||
// saturationFactor = 1.0 + (shadowsValue / 300.0) * (1.0 - lum / shadowThreshold);
|
||||
// }
|
||||
|
||||
// Reduce saturation slightly when recovering highlights (like Lightroom)
|
||||
if (highlightsValue > 0.0 && lum > highlightThreshold) {
|
||||
saturationFactor *= 1.0 - (highlightsValue / 400.0) * ((lum - highlightThreshold) / (1.0 - highlightThreshold));
|
||||
}
|
||||
|
||||
// Apply saturation adjustment while preserving luminance
|
||||
return mix(vec3(newLum), adjusted, saturationFactor);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(InputTexture, TexCoord);
|
||||
color.rgb = applyHighlightsShadows(color.rgb);
|
||||
FragColor = vec4(clamp(color.rgb, 0.0, 1.0), color.a);
|
||||
}
|
45
shaders/histogram.comp
Normal file
45
shaders/histogram.comp
Normal file
@ -0,0 +1,45 @@
|
||||
#version 430 core // Need SSBOs and atomic counters
|
||||
|
||||
// Input Texture (the processed image ready for display)
|
||||
// Binding = 0 matches glBindImageTexture unit
|
||||
// Use rgba8 format as we assume display texture is 8-bit sRGB (adjust if needed)
|
||||
layout(binding = 0, rgba8) uniform readonly image2D InputTexture;
|
||||
|
||||
// Output Histogram Buffer (SSBO)
|
||||
// Binding = 1 matches glBindBufferBase index
|
||||
// Contains 256 bins for R, 256 for G, 256 for B sequentially (total 768 uints)
|
||||
layout(std430, binding = 1) buffer HistogramBuffer {
|
||||
uint bins[]; // Use an unsized array
|
||||
} histogram;
|
||||
|
||||
// Workgroup size (adjust based on GPU architecture for performance, 16x16 is often reasonable)
|
||||
layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
|
||||
|
||||
void main() {
|
||||
// Get the global invocation ID (like pixel coordinates)
|
||||
ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 textureSize = imageSize(InputTexture);
|
||||
|
||||
// Boundary check: Don't process outside the image bounds
|
||||
if (pixelCoord.x >= textureSize.x || pixelCoord.y >= textureSize.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the pixel color (values are normalized float 0.0-1.0 from rgba8 image load)
|
||||
vec4 pixelColor = imageLoad(InputTexture, pixelCoord);
|
||||
|
||||
// Calculate bin indices (0-255)
|
||||
// We clamp just in case, although imageLoad from rgba8 should be in range.
|
||||
uint rBin = uint(clamp(pixelColor.r, 0.0, 1.0) * 255.0);
|
||||
uint gBin = uint(clamp(pixelColor.g, 0.0, 1.0) * 255.0);
|
||||
uint bBin = uint(clamp(pixelColor.b, 0.0, 1.0) * 255.0);
|
||||
|
||||
// Atomically increment the counters in the SSBO
|
||||
// Offset Green bins by 256, Blue bins by 512
|
||||
atomicAdd(histogram.bins[rBin], 1u);
|
||||
atomicAdd(histogram.bins[gBin + 256u], 1u);
|
||||
atomicAdd(histogram.bins[bBin + 512u], 1u);
|
||||
|
||||
// Optional: Track Max Value (more complex, requires another SSBO or different strategy)
|
||||
// Example: atomicMax(histogram.maxBinValue, histogram.bins[rBin]); // Needs careful sync
|
||||
}
|
16
shaders/linear_to_srgb.frag
Normal file
16
shaders/linear_to_srgb.frag
Normal file
@ -0,0 +1,16 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
uniform sampler2D InputTexture;
|
||||
|
||||
vec3 linearToSrgb(vec3 linearRGB) {
|
||||
linearRGB = max(linearRGB, vec3(0.0)); // Ensure non-negative input
|
||||
vec3 srgb = pow(linearRGB, vec3(1.0/2.4));
|
||||
srgb = mix(linearRGB * 12.92, srgb * 1.055 - 0.055, step(vec3(0.0031308), linearRGB));
|
||||
return clamp(srgb, 0.0, 1.0); // Clamp final output to display range
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 linearColor = texture(InputTexture, TexCoord).rgb;
|
||||
FragColor = vec4(linearToSrgb(linearColor), texture(InputTexture, TexCoord).a);
|
||||
}
|
9
shaders/passthrough.frag
Normal file
9
shaders/passthrough.frag
Normal file
@ -0,0 +1,9 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
|
||||
void main() {
|
||||
FragColor = texture(InputTexture, TexCoord);
|
||||
}
|
10
shaders/passthrough.vert
Normal file
10
shaders/passthrough.vert
Normal file
@ -0,0 +1,10 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 aPos;
|
||||
layout (location = 1) in vec2 aTexCoord;
|
||||
|
||||
out vec2 TexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);
|
||||
TexCoord = aTexCoord;
|
||||
}
|
44
shaders/saturation.frag
Normal file
44
shaders/saturation.frag
Normal file
@ -0,0 +1,44 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float saturationValue; // -100 (grayscale) to 100 (double sat)
|
||||
|
||||
// Perceptual luminance weights (Rec. 709)
|
||||
const vec3 luminanceWeight = vec3(0.2126, 0.7152, 0.0722);
|
||||
|
||||
vec3 applySaturation(vec3 color, float saturation) {
|
||||
// Get original luminance
|
||||
float lum = dot(color, luminanceWeight);
|
||||
|
||||
// Skip processing for very dark or very bright pixels
|
||||
if (lum < 0.001 || lum > 0.999) return color;
|
||||
|
||||
// Non-linear saturation response curve (more natural-looking)
|
||||
float factor;
|
||||
if (saturation >= 0.0) {
|
||||
// Positive saturation with highlight protection
|
||||
factor = 1.0 + (saturation / 100.0) * (1.0 - 0.3 * smoothstep(0.7, 1.0, lum));
|
||||
} else {
|
||||
// Negative saturation with shadow protection
|
||||
factor = 1.0 + (saturation / 100.0) * (1.0 - 0.1 * smoothstep(0.0, 0.2, lum));
|
||||
}
|
||||
|
||||
// Apply saturation while preserving luminance
|
||||
vec3 adjusted = mix(vec3(lum), color, factor);
|
||||
|
||||
// Ensure we maintain original luminance exactly
|
||||
float newLum = dot(adjusted, luminanceWeight);
|
||||
return adjusted * (lum / max(newLum, 0.001));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(InputTexture, TexCoord);
|
||||
|
||||
// Apply saturation
|
||||
color.rgb = applySaturation(color.rgb, saturationValue);
|
||||
|
||||
// Proper clamping to valid range
|
||||
FragColor = vec4(clamp(color.rgb, 0.0, 1.0), color.a);
|
||||
}
|
16
shaders/srgb_to_linear.frag
Normal file
16
shaders/srgb_to_linear.frag
Normal file
@ -0,0 +1,16 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
uniform sampler2D InputTexture;
|
||||
|
||||
vec3 linearToSrgb(vec3 linearRGB) {
|
||||
linearRGB = max(linearRGB, vec3(0.0)); // Ensure non-negative input
|
||||
vec3 srgb = pow(linearRGB, vec3(1.0/2.4));
|
||||
srgb = mix(linearRGB * 12.92, srgb * 1.055 - 0.055, step(vec3(0.0031308), linearRGB));
|
||||
return clamp(srgb, 0.0, 1.0); // Clamp final output to display range
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 linearColor = texture(InputTexture, TexCoord).rgb;
|
||||
FragColor = vec4(linearToSrgb(linearColor), texture(InputTexture, TexCoord).a);
|
||||
}
|
121
shaders/texture.frag
Normal file
121
shaders/texture.frag
Normal file
@ -0,0 +1,121 @@
|
||||
#version 330 core
|
||||
/*
|
||||
* Advanced Texture Control Shader
|
||||
* -------------------------------
|
||||
* This shader emulates Adobe Lightroom/Photoshop's Texture slider by:
|
||||
*
|
||||
* 1. Using frequency separation techniques to isolate medium-frequency details
|
||||
* 2. Employing multi-scale edge detection for more natural enhancement
|
||||
* 3. Adding tone-aware processing to protect highlights and shadows
|
||||
* 4. Implementing perceptual weighting to preserve color relationships
|
||||
* 5. Using non-linear response curves similar to Lightroom's implementation
|
||||
*
|
||||
* Negative values: Smooth medium-frequency texture details (skin smoothing)
|
||||
* Positive values: Enhance medium-frequency texture details (fabric, hair, etc.)
|
||||
*
|
||||
* Unlike Clarity (which affects larger contrast structures) or Sharpening (which
|
||||
* affects fine details), Texture specifically targets medium-frequency details
|
||||
* that represent surface textures while avoiding edges.
|
||||
*/
|
||||
|
||||
float luminance(vec3 color) {
|
||||
return dot(color, vec3(0.2126, 0.7152, 0.0722));
|
||||
}
|
||||
|
||||
// Improved blur function with Gaussian weights for better quality
|
||||
vec3 gaussianBlur(sampler2D tex, vec2 uv, vec2 texelSize, float radius) {
|
||||
vec3 blurred = vec3(0.0);
|
||||
float weightSum = 0.0;
|
||||
float sigma = radius / 2.0;
|
||||
float sigma2 = 2.0 * sigma * sigma;
|
||||
int kernelSize = int(ceil(radius * 2.0));
|
||||
|
||||
for (int x = -kernelSize; x <= kernelSize; ++x) {
|
||||
for (int y = -kernelSize; y <= kernelSize; ++y) {
|
||||
float dist2 = float(x*x + y*y);
|
||||
float weight = exp(-dist2 / sigma2);
|
||||
vec3 sample = texture(tex, uv + vec2(x, y) * texelSize).rgb;
|
||||
blurred += sample * weight;
|
||||
weightSum += weight;
|
||||
}
|
||||
}
|
||||
|
||||
return blurred / max(weightSum, 0.0001);
|
||||
}
|
||||
|
||||
// Edge-aware weight function to prevent halos
|
||||
float edgeWeight(float lumaDiff, float threshold) {
|
||||
return 1.0 - smoothstep(0.0, threshold, abs(lumaDiff));
|
||||
}
|
||||
|
||||
// Sigmoid function for smoother transitions
|
||||
float sigmoid(float x, float strength) {
|
||||
return x * (1.0 + strength) / (1.0 + strength * abs(x));
|
||||
}
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float textureValue; // -100 (smooth) to 100 (enhance)
|
||||
|
||||
vec3 applyTexture(vec3 originalColor, vec2 uv, float textureAdj) {
|
||||
if (abs(textureAdj) < 0.01) return originalColor;
|
||||
|
||||
vec2 texelSize = 1.0 / textureSize(InputTexture, 0);
|
||||
float origLuma = luminance(originalColor);
|
||||
|
||||
// Multi-scale approach for medium frequency targeting
|
||||
// For texture, we want medium frequencies (not too small, not too large)
|
||||
float smallRadius = 2.0; // For high frequency details
|
||||
float mediumRadius = 4.0; // For medium frequency details (texture)
|
||||
float largeRadius = 8.0; // For low frequency details
|
||||
|
||||
vec3 smallBlur = gaussianBlur(InputTexture, uv, texelSize, smallRadius);
|
||||
vec3 mediumBlur = gaussianBlur(InputTexture, uv, texelSize, mediumRadius);
|
||||
vec3 largeBlur = gaussianBlur(InputTexture, uv, texelSize, largeRadius);
|
||||
|
||||
// Extract medium frequencies (texture details)
|
||||
vec3 highFreq = smallBlur - mediumBlur;
|
||||
vec3 mediumFreq = mediumBlur - largeBlur;
|
||||
|
||||
// Calculate local contrast for edge-aware processing
|
||||
float smallLuma = luminance(smallBlur);
|
||||
float mediumLuma = luminance(mediumBlur);
|
||||
float largeLuma = luminance(largeBlur);
|
||||
|
||||
// Edge detection weights
|
||||
float edgeMask = edgeWeight(smallLuma - mediumLuma, 0.1);
|
||||
|
||||
// Highlight & shadow protection
|
||||
float highlightProtect = 1.0 - smoothstep(0.75, 0.95, origLuma);
|
||||
float shadowProtect = smoothstep(0.05, 0.25, origLuma);
|
||||
float tonalWeight = highlightProtect * shadowProtect;
|
||||
|
||||
// Map texture value to a perceptually balanced strength
|
||||
// Use non-linear mapping to match Lightroom's response curve
|
||||
float strength = sign(textureAdj) * pow(abs(textureAdj / 100.0), 0.8);
|
||||
|
||||
// Apply different processing for positive vs negative values
|
||||
vec3 result = originalColor;
|
||||
if (strength > 0.0) {
|
||||
// Enhance texture (positive values)
|
||||
float enhanceFactor = strength * 1.5 * tonalWeight * edgeMask;
|
||||
result = originalColor + mediumFreq * enhanceFactor;
|
||||
} else {
|
||||
// Smooth texture (negative values)
|
||||
float smoothFactor = -strength * tonalWeight;
|
||||
result = mix(originalColor, mediumBlur, smoothFactor);
|
||||
}
|
||||
|
||||
// Apply sigmoid function for more natural transitions
|
||||
result = mix(originalColor, result, sigmoid(abs(strength), 0.5));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(InputTexture, TexCoord);
|
||||
color.rgb = applyTexture(color.rgb, TexCoord, textureValue);
|
||||
FragColor = vec4(max(color.rgb, vec3(0.0)), color.a);
|
||||
}
|
79
shaders/vibrance.frag
Normal file
79
shaders/vibrance.frag
Normal file
@ -0,0 +1,79 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float vibranceValue; // -100 to 100
|
||||
|
||||
const vec3 luminanceWeight = vec3(0.2126, 0.7152, 0.0722);
|
||||
|
||||
// Check if a color is potentially a skin tone (approximate)
|
||||
float skinToneLikelihood(vec3 color) {
|
||||
// Simple skin tone detection - check if in common skin tone range
|
||||
// Based on normalized r/g ratio in RGB space
|
||||
float total = color.r + color.g + color.b;
|
||||
if (total < 0.001) return 0.0;
|
||||
|
||||
vec3 normalized = color / total;
|
||||
|
||||
// Detect skin tones based on red-green ratio and absolute red value
|
||||
bool redEnough = normalized.r > 0.35;
|
||||
bool redGreenRatio = normalized.r / normalized.g > 1.1 && normalized.r / normalized.g < 2.0;
|
||||
bool notTooBlue = normalized.b < 0.4;
|
||||
|
||||
return (redEnough && redGreenRatio && notTooBlue) ? 1.0 - pow(normalized.b * 1.5, 2.0) : 0.0;
|
||||
}
|
||||
|
||||
vec3 applyVibrance(vec3 color, float vibrance) {
|
||||
float vibAmount = vibrance / 100.0; // Map to -1..1
|
||||
|
||||
// Calculate better saturation
|
||||
float luma = dot(color, luminanceWeight);
|
||||
vec3 chroma = max(color - luma, 0.0);
|
||||
float sat = length(chroma) / (luma + 0.001);
|
||||
|
||||
// Get skin tone protection factor
|
||||
float skinFactor = skinToneLikelihood(color);
|
||||
|
||||
// Calculate adjustment strength based on current saturation
|
||||
// Less effect on already highly saturated colors
|
||||
float satWeight = 1.0 - smoothstep(0.2, 0.8, sat);
|
||||
|
||||
// Apply less vibrance to skin tones
|
||||
float adjustmentFactor = satWeight * (1.0 - skinFactor * 0.7);
|
||||
|
||||
// Create non-linear response curve for natural-looking adjustment
|
||||
float strength = vibAmount > 0.0
|
||||
? vibAmount * (1.0 - pow(sat, 2.0)) // Positive vibrance
|
||||
: vibAmount; // Negative vibrance (desaturation)
|
||||
|
||||
// Fine-tune the saturation component-wise to preserve color relationships
|
||||
vec3 satColor = color;
|
||||
if (abs(vibAmount) > 0.001) {
|
||||
// Get distance from gray for each channel
|
||||
vec3 dist = color - luma;
|
||||
|
||||
// Adjust distance based on vibrance
|
||||
dist *= (1.0 + strength * adjustmentFactor);
|
||||
|
||||
// Rebuild color from luma + adjusted chroma
|
||||
satColor = luma + dist;
|
||||
|
||||
// Preserve color ratios for extreme adjustments
|
||||
if (vibAmount > 0.5) {
|
||||
float maxComponent = max(satColor.r, max(satColor.g, satColor.b));
|
||||
if (maxComponent > 1.0) {
|
||||
float scale = min(1.0 / maxComponent, 2.0); // Limit scaling
|
||||
satColor = luma + (satColor - luma) * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return satColor;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(InputTexture, TexCoord);
|
||||
color.rgb = applyVibrance(color.rgb, vibranceValue);
|
||||
FragColor = vec4(max(color.rgb, vec3(0.0)), color.a);
|
||||
}
|
158
shaders/white_balance.frag
Normal file
158
shaders/white_balance.frag
Normal file
@ -0,0 +1,158 @@
|
||||
#version 330 core
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float temperature; // Target Correlated Color Temperature (Kelvin, e.g., 1000-20000)
|
||||
uniform float tint; // Green-Magenta shift (-100 to +100)
|
||||
|
||||
// --- Constants ---
|
||||
|
||||
// sRGB Primaries to XYZ (D65)
|
||||
const mat3 M_RGB_2_XYZ_D65 = mat3(
|
||||
0.4124564, 0.3575761, 0.1804375,
|
||||
0.2126729, 0.7151522, 0.0721750,
|
||||
0.0193339, 0.1191920, 0.9503041
|
||||
);
|
||||
|
||||
// XYZ (D65) to sRGB Primaries
|
||||
const mat3 M_XYZ_2_RGB_D65 = mat3(
|
||||
3.2404542, -1.5371385, -0.4985314,
|
||||
-0.9692660, 1.8760108, 0.0415560,
|
||||
0.0556434, -0.2040259, 1.0572252
|
||||
);
|
||||
|
||||
// Standard Illuminant D65 XYZ (Normalized Y=1) - Our Target White
|
||||
const vec3 WHITEPOINT_D65 = vec3(0.95047, 1.00000, 1.08883);
|
||||
|
||||
// Bradford CAT Matrices
|
||||
const mat3 M_BRADFORD = mat3(
|
||||
0.8951, 0.2664, -0.1614,
|
||||
-0.7502, 1.7135, 0.0367,
|
||||
0.0389, -0.0685, 1.0296
|
||||
);
|
||||
const mat3 M_BRADFORD_INV = mat3(
|
||||
0.9869929, -0.1470543, 0.1599627,
|
||||
0.4323053, 0.5183603, 0.0492912,
|
||||
-0.0085287, 0.0400428, 0.9684867
|
||||
);
|
||||
|
||||
// --- Helper Functions ---
|
||||
|
||||
// Approximate Kelvin Temperature to XYZ Chromaticity (xy) -> XYZ coordinates
|
||||
// Uses simplified polynomial fits for different temperature ranges.
|
||||
// Note: More accurate methods often use look-up tables or spectral calculations.
|
||||
vec3 kelvinToXYZ(float kelvin) {
|
||||
float temp = clamp(kelvin, 1000.0, 20000.0);
|
||||
float x, y;
|
||||
|
||||
// Calculate xy chromaticity coordinates based on temperature
|
||||
// Formulas from: http://www.brucelindbloom.com/index.html?Eqn_T_to_xy.html (with slight adaptations)
|
||||
float t = temp;
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
|
||||
// Calculate x coordinate
|
||||
if (t >= 1000.0 && t <= 4000.0) {
|
||||
x = -0.2661239e9 / t3 - 0.2343589e6 / t2 + 0.8776956e3 / t + 0.179910;
|
||||
} else { // t > 4000.0 && t <= 25000.0
|
||||
x = -3.0258469e9 / t3 + 2.1070379e6 / t2 + 0.2226347e3 / t + 0.240390;
|
||||
}
|
||||
|
||||
// Calculate y coordinate based on x
|
||||
float x2 = x * x;
|
||||
float x3 = x2 * x;
|
||||
if (t >= 1000.0 && t <= 2222.0) {
|
||||
y = -1.1063814 * x3 - 1.34811020 * x2 + 2.18555832 * x - 0.18709;
|
||||
} else if (t > 2222.0 && t <= 4000.0) {
|
||||
y = -0.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - 0.16748867;
|
||||
} else { // t > 4000.0 && t <= 25000.0
|
||||
y = 3.0817580 * x3 - 5.8733867 * x2 + 3.75112997 * x - 0.37001483;
|
||||
}
|
||||
|
||||
// Convert xyY (Y=1) to XYZ
|
||||
if (y < 1e-6) return vec3(0.0); // Avoid division by zero
|
||||
float Y = 1.0;
|
||||
float X = (x / y) * Y;
|
||||
float Z = ((1.0 - x - y) / y) * Y;
|
||||
|
||||
return vec3(X, Y, Z);
|
||||
}
|
||||
|
||||
// Apply Bradford Chromatic Adaptation Transform
|
||||
vec3 adaptXYZ(vec3 xyzColor, vec3 sourceWhiteXYZ, vec3 destWhiteXYZ) {
|
||||
vec3 sourceCone = M_BRADFORD * sourceWhiteXYZ;
|
||||
vec3 destCone = M_BRADFORD * destWhiteXYZ;
|
||||
|
||||
// Avoid division by zero if source cone response is zero
|
||||
// (shouldn't happen with typical white points but good practice)
|
||||
if (sourceCone.r < 1e-6 || sourceCone.g < 1e-6 || sourceCone.b < 1e-6) {
|
||||
return xyzColor; // Return original color if adaptation is impossible
|
||||
}
|
||||
|
||||
vec3 ratio = destCone / sourceCone;
|
||||
|
||||
mat3 adaptationMatrix = M_BRADFORD_INV * mat3(ratio.x, 0, 0, 0, ratio.y, 0, 0, 0, ratio.z) * M_BRADFORD;
|
||||
|
||||
return adaptationMatrix * xyzColor;
|
||||
}
|
||||
|
||||
// --- Main White Balance Function ---
|
||||
vec3 applyWhiteBalance(vec3 linearSRGBColor, float tempK, float tintVal) {
|
||||
|
||||
// 1. Convert input Linear sRGB (D65) to XYZ D65
|
||||
vec3 inputXYZ = M_RGB_2_XYZ_D65 * linearSRGBColor;
|
||||
|
||||
// 2. Calculate the XYZ white point of the source illuminant (from Kelvin temp)
|
||||
// This is the white we want to adapt *FROM*
|
||||
vec3 sourceWhiteXYZ = kelvinToXYZ(tempK);
|
||||
|
||||
// 3. Apply Bradford CAT to adapt from sourceWhiteXYZ to D65
|
||||
vec3 adaptedXYZ = adaptXYZ(inputXYZ, sourceWhiteXYZ, WHITEPOINT_D65);
|
||||
|
||||
// 4. Convert adapted XYZ (now relative to D65) back to Linear sRGB
|
||||
vec3 adaptedLinearSRGB = M_XYZ_2_RGB_D65 * adaptedXYZ;
|
||||
|
||||
// 5. Apply Tint adjustment (Post-CAT approximation)
|
||||
// This shifts color balance along a Green<->Magenta axis.
|
||||
// We scale RGB components slightly based on the tint value.
|
||||
// A common method is to affect Green opposite to Red/Blue.
|
||||
if (abs(tintVal) > 0.01) {
|
||||
// Map tint -100..100 to a scaling factor, e.g., -0.1..0.1
|
||||
float tintFactor = tintVal / 1000.0; // Smaller scale factor for subtle tint
|
||||
|
||||
// Apply scaling: Increase G for negative tint, Decrease G for positive tint
|
||||
// Compensate R and B slightly in the opposite direction.
|
||||
// Coefficients below are heuristic and may need tuning for perceptual feel.
|
||||
float rScale = 1.0 + tintFactor * 0.5;
|
||||
float gScale = 1.0 - tintFactor * 1.0;
|
||||
float bScale = 1.0 + tintFactor * 0.5;
|
||||
|
||||
vec3 tintScaleVec = vec3(rScale, gScale, bScale);
|
||||
|
||||
// Optional: Normalize scale vector to preserve luminance (roughly)
|
||||
// Calculate luminance of the scale vector itself
|
||||
float scaleLum = dot(tintScaleVec, vec3(0.2126, 0.7152, 0.0722));
|
||||
if (scaleLum > 1e-5) {
|
||||
tintScaleVec /= scaleLum; // Normalize
|
||||
}
|
||||
|
||||
adaptedLinearSRGB *= tintScaleVec;
|
||||
}
|
||||
|
||||
return adaptedLinearSRGB;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(InputTexture, TexCoord);
|
||||
vec3 linearInputColor = texColor.rgb; // Assuming input texture is already linear sRGB
|
||||
|
||||
// Calculate white balanced color
|
||||
vec3 whiteBalancedColor = applyWhiteBalance(linearInputColor, temperature, tint);
|
||||
|
||||
// Ensure output is non-negative
|
||||
whiteBalancedColor = max(whiteBalancedColor, vec3(0.0));
|
||||
|
||||
FragColor = vec4(whiteBalancedColor, texColor.a);
|
||||
}
|
51
shaders/whites_blacks.frag
Normal file
51
shaders/whites_blacks.frag
Normal file
@ -0,0 +1,51 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D InputTexture;
|
||||
uniform float whitesValue; // -100 (pull down) to 100 (push up)
|
||||
uniform float blacksValue; // -100 (lift up) to 100 (push down)
|
||||
|
||||
const vec3 luminanceWeight = vec3(0.2126, 0.7152, 0.0722);
|
||||
|
||||
// Helper function to preserve color relationships when adjusting luminance
|
||||
vec3 preserveColor(vec3 color, float newLum) {
|
||||
float oldLum = dot(color, luminanceWeight);
|
||||
return oldLum > 0.0 ? color * (newLum / oldLum) : vec3(newLum);
|
||||
}
|
||||
|
||||
vec3 applyWhitesBlacks(vec3 color, float whites, float blacks) {
|
||||
float lum = dot(color, luminanceWeight);
|
||||
|
||||
// Map slider values to more appropriate adjustment strengths
|
||||
float whitesStrength = whites / 100.0;
|
||||
float blacksStrength = blacks / 100.0;
|
||||
|
||||
// Create better perceptual masks with wider, smoother influence
|
||||
// Whites affect primarily highlights but have some influence into midtones
|
||||
float whiteMask = smoothstep(0.25, 1.0, lum);
|
||||
whiteMask = pow(whiteMask, 2.0 - max(0.0, whitesStrength)); // Dynamic falloff
|
||||
|
||||
// Blacks affect primarily shadows but have some influence into midtones
|
||||
float blackMask = 1.0 - smoothstep(0.0, 0.5, lum);
|
||||
blackMask = pow(blackMask, 2.0 - max(0.0, -blacksStrength)); // Dynamic falloff
|
||||
|
||||
// Calculate adjustment curves with proper toe/shoulder response
|
||||
float whitesAdj = 1.0 + whitesStrength * whiteMask * (1.0 - pow(1.0 - whiteMask, 3.0));
|
||||
float blacksAdj = 1.0 - blacksStrength * blackMask * (1.0 - pow(1.0 - blackMask, 3.0));
|
||||
|
||||
// Apply adjustments with color preservation
|
||||
float adjustedLum = lum * whitesAdj * blacksAdj;
|
||||
adjustedLum = clamp(adjustedLum, 0.0, 2.0); // Allow some headroom for highlights
|
||||
|
||||
// Preserve color relationships by scaling RGB proportionally
|
||||
vec3 result = preserveColor(color, adjustedLum);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(InputTexture, TexCoord);
|
||||
color.rgb = applyWhitesBlacks(color.rgb, whitesValue, blacksValue);
|
||||
FragColor = vec4(max(color.rgb, vec3(0.0)), color.a); // Ensure non-negative
|
||||
}
|
101
shaderutils.h
Normal file
101
shaderutils.h
Normal file
@ -0,0 +1,101 @@
|
||||
// shader_utils.h
|
||||
#ifndef SHADER_UTILS_H
|
||||
#define SHADER_UTILS_H
|
||||
|
||||
#include <string>
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <SDL_opengles2.h>
|
||||
#else
|
||||
#include <GL/glew.h>
|
||||
#include <SDL_opengl.h>
|
||||
#endif
|
||||
|
||||
|
||||
GLuint LoadShaderProgram(const std::string& vsSource, const std::string& fsSource);
|
||||
GLuint LoadShaderProgramFromFiles(const std::string& vsPath, const std::string& fsPath);
|
||||
|
||||
#endif // SHADER_UTILS_H
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
// Function to compile a shader
|
||||
GLuint CompileShader(GLenum type, const std::string& source) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
const char* src = source.c_str();
|
||||
glShaderSource(shader, 1, &src, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
GLint logLength;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
|
||||
std::vector<char> log(logLength);
|
||||
glGetShaderInfoLog(shader, logLength, nullptr, log.data());
|
||||
std::cerr << "ERROR::SHADER::COMPILATION_FAILED\n" << (type == GL_VERTEX_SHADER ? "Vertex" : "Fragment") << "\n" << log.data() << std::endl;
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
// Function to link shaders into a program
|
||||
GLuint LinkProgram(GLuint vertexShader, GLuint fragmentShader) {
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vertexShader);
|
||||
glAttachShader(program, fragmentShader);
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
GLint logLength;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
|
||||
std::vector<char> log(logLength);
|
||||
glGetProgramInfoLog(program, logLength, nullptr, log.data());
|
||||
std::cerr << "ERROR::PROGRAM::LINKING_FAILED\n" << log.data() << std::endl;
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
|
||||
GLuint LoadShaderProgram(const std::string& vsSource, const std::string& fsSource) {
|
||||
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
|
||||
if (!vs) return 0;
|
||||
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
|
||||
if (!fs) { glDeleteShader(vs); return 0; }
|
||||
|
||||
GLuint program = LinkProgram(vs, fs);
|
||||
|
||||
// Shaders can be deleted after linking
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
|
||||
std::string ReadFile(const std::string& path) {
|
||||
std::ifstream file(path);
|
||||
if (!file) {
|
||||
std::cerr << "Error opening file: " << path << std::endl;
|
||||
return "";
|
||||
}
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
GLuint LoadShaderProgramFromFiles(const std::string& vsPath, const std::string& fsPath) {
|
||||
std::string vsSource = ReadFile(vsPath);
|
||||
std::string fsSource = ReadFile(fsPath);
|
||||
if (vsSource.empty() || fsSource.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return LoadShaderProgram(vsSource, fsSource);
|
||||
}
|
720
tex_inspect_opengl.cpp
Normal file
720
tex_inspect_opengl.cpp
Normal file
@ -0,0 +1,720 @@
|
||||
// ImGuiTexInspect, a texture inspector widget for dear imgui
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#if defined EMSCRIPTEN && defined IMGUI_TEX_INSPECT_FLOAT_READ_ENABLED
|
||||
#warning "Float texture read back is disabled on Emscripten"
|
||||
#undef IMGUI_TEX_INSPECT_FLOAT_READ_ENABLED
|
||||
#endif
|
||||
#include "imgui_tex_inspect_internal.h"
|
||||
|
||||
// ==========================================================================
|
||||
// This file is largely based on:
|
||||
// https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_opengl3.cpp
|
||||
//
|
||||
// In the following section the ImGui_ImplOpenGL3_Init function has been
|
||||
// changed to not rewrite global ImGui state. It has also been wrapped in a
|
||||
// namespace to not clash with the main ImGui version. Aside from that this
|
||||
// section is identical to the imgui original.
|
||||
//
|
||||
// It's reproduced here because none of this code is exposed in the ImGui API
|
||||
// in a way that be reused (nor should it be).
|
||||
//
|
||||
// Search for "END COPIED" to find the end of the copied segment.
|
||||
// ===========================================================================
|
||||
|
||||
// COPIED FROM imgui_impl_opengl3.cp ////////////////////////////////////////
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <stdio.h>
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
|
||||
#include <stddef.h> // intptr_t
|
||||
#else
|
||||
#include <stdint.h> // intptr_t
|
||||
#endif
|
||||
|
||||
// GL includes
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <GLES2/gl2.h>
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
|
||||
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
|
||||
#else
|
||||
#include <GLES3/gl3.h> // Use GL ES 3
|
||||
#endif
|
||||
#else
|
||||
// About Desktop OpenGL function loaders:
|
||||
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
|
||||
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
|
||||
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
|
||||
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
|
||||
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
|
||||
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code.
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
|
||||
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code.
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
|
||||
#include <glad/gl.h> // Needs to be initialized with gladLoadGL(...) or gladLoaderLoadGL() in user's code.
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
|
||||
#ifndef GLFW_INCLUDE_NONE
|
||||
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
|
||||
#endif
|
||||
#include <glbinding/Binding.h> // Needs to be initialized with glbinding::Binding::initialize() in user's code.
|
||||
#include <glbinding/gl/gl.h>
|
||||
using namespace gl;
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
|
||||
#ifndef GLFW_INCLUDE_NONE
|
||||
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
|
||||
#endif
|
||||
#include <glbinding/glbinding.h>// Needs to be initialized with glbinding::initialize() in user's code.
|
||||
#include <glbinding/gl/gl.h>
|
||||
using namespace gl;
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_EPOXY)
|
||||
#include <epoxy/gl.h>
|
||||
#else
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <SDL_opengles2.h>
|
||||
#else
|
||||
#include <SDL_opengl.h>
|
||||
#include <SDL2/SDL_opengl.h>
|
||||
#include <SDL2/SDL_opengles2.h>
|
||||
#include <SDL2/SDL_opengles2_gl2.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2)
|
||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
#endif
|
||||
|
||||
// Desktop GL 3.3+ has glBindSampler()
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_3)
|
||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
#endif
|
||||
|
||||
// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
|
||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||
#endif
|
||||
|
||||
namespace imgui_impl_opengl
|
||||
{
|
||||
// OpenGL Data
|
||||
static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
|
||||
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
|
||||
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
|
||||
static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
|
||||
static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0; // Vertex attributes location
|
||||
static GLint g_UniformLocationForceNearestSampling = 0;
|
||||
static GLint g_UniformLocationGridWidth = 0;
|
||||
|
||||
// Functions
|
||||
static bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||
{
|
||||
// Query for GL version (e.g. 320 for GL 3.2)
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
GLint major = 0;
|
||||
GLint minor = 0;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
||||
if (major == 0 && minor == 0)
|
||||
{
|
||||
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
|
||||
const char* gl_version = (const char*)glGetString(GL_VERSION);
|
||||
sscanf(gl_version, "%d.%d", &major, &minor);
|
||||
}
|
||||
g_GlVersion = (GLuint)(major * 100 + minor * 10);
|
||||
#else
|
||||
g_GlVersion = 200; // GLES 2
|
||||
#endif
|
||||
|
||||
|
||||
// Store GLSL version string so we can refer to it later in case we recreate shaders.
|
||||
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 100";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 300 es";
|
||||
#elif defined(__APPLE__)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 150";
|
||||
#else
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 130";
|
||||
#endif
|
||||
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
|
||||
strcpy(g_GlslVersionString, glsl_version);
|
||||
strcat(g_GlslVersionString, "\n");
|
||||
|
||||
// Debugging construct to make it easily visible in the IDE and debugger which GL loader has been selected.
|
||||
// The code actually never uses the 'gl_loader' variable! It is only here so you can read it!
|
||||
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
|
||||
// you are likely to get a crash below.
|
||||
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
|
||||
const char* gl_loader = "Unknown";
|
||||
IM_UNUSED(gl_loader);
|
||||
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
|
||||
gl_loader = "GL3W";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
|
||||
gl_loader = "GLEW";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
|
||||
gl_loader = "GLAD";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
|
||||
gl_loader = "GLAD2";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
|
||||
gl_loader = "glbinding2";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
|
||||
gl_loader = "glbinding3";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||
gl_loader = "custom";
|
||||
#else
|
||||
gl_loader = "none";
|
||||
#endif
|
||||
|
||||
// Make an arbitrary GL call (we don't actually need the result)
|
||||
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
|
||||
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
|
||||
GLint current_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// COPIED FROM A DIFFERENT PART OF imgui_impl_opengl3.cpp
|
||||
// ===========================================================================
|
||||
|
||||
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
|
||||
static bool CheckShader(GLuint handle, const char* desc)
|
||||
{
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
|
||||
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
|
||||
static bool CheckProgram(GLuint handle, const char* desc)
|
||||
{
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetProgramiv(handle, GL_LINK_STATUS, &status);
|
||||
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height)
|
||||
{
|
||||
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||
if (g_GlVersion >= 310)
|
||||
glDisable(GL_PRIMITIVE_RESTART);
|
||||
#endif
|
||||
#ifdef GL_POLYGON_MODE
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#endif
|
||||
|
||||
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
|
||||
#if defined(GL_CLIP_ORIGIN)
|
||||
bool clip_origin_lower_left = true;
|
||||
if (g_GlVersion >= 450)
|
||||
{
|
||||
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin);
|
||||
if (current_clip_origin == GL_UPPER_LEFT)
|
||||
clip_origin_lower_left = false;
|
||||
}
|
||||
#endif
|
||||
// Setup viewport, orthographic projection matrix
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
#if defined(GL_CLIP_ORIGIN)
|
||||
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
|
||||
#endif
|
||||
const float ortho_projection[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, -1.0f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
|
||||
};
|
||||
glUseProgram(g_ShaderHandle);
|
||||
glUniform1i(g_AttribLocationTex, 0);
|
||||
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||
|
||||
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
// if (g_GlVersion >= 330)
|
||||
// glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
|
||||
#endif
|
||||
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxPos);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxUV);
|
||||
//glEnableVertexAttribArray(g_AttribLocationVtxColor); //Our shader doesn't use vertex color
|
||||
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
|
||||
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
|
||||
//glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// END COPIED FROM imgui_impl_opengl3.cpp
|
||||
// ---------------------------------------------------------------------------
|
||||
// Note that a lot of the following code still orginated in
|
||||
// imgui_impl_opengl3.cpp but there are more changes from here on.
|
||||
// ===========================================================================
|
||||
|
||||
// New uniforms for ImGuiTexInspect fragment shader
|
||||
static GLint g_UniformLocationTextureSize;
|
||||
static GLint g_UniformLocationColorTransform;
|
||||
static GLint g_UniformLocationColorOffset;
|
||||
static GLint g_UniformLocationBackgroundColor;
|
||||
static GLint g_UniformLocationPremultiplyAlpha;
|
||||
static GLint g_UniformLocationDisableFinalAlpha;
|
||||
static GLint g_UniformLocationGrid;
|
||||
|
||||
|
||||
// Vertex shaders are directly from imgui_impl_opengl3.cpp
|
||||
const GLchar* vertex_shader_glsl_120 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"attribute vec2 Position;\n"
|
||||
"attribute vec2 UV;\n"
|
||||
"attribute vec4 Color;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"varying vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_130 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"in vec2 Position;\n"
|
||||
"in vec2 UV;\n"
|
||||
"in vec4 Color;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_300_es =
|
||||
"precision mediump float;\n"
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_410_core =
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] IMGUI_TEX_INSPECT FRAGMENT SHADERS
|
||||
//-------------------------------------------------------------------------
|
||||
const GLchar *fragment_shader_glsl_120 = "#ifdef GL_ES\n"
|
||||
" precision mediump float;\n"
|
||||
"#endif\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"uniform vec2 TextureSize;\n"
|
||||
"uniform mat4 ColorTransform;\n"
|
||||
"uniform vec4 ColorOffset;\n"
|
||||
"uniform vec3 BackgroundColor;\n"
|
||||
"uniform float PremultiplyAlpha;\n"
|
||||
"uniform float DisableFinalAlpha;\n"
|
||||
"uniform bool ForceNearestSampling;\n"
|
||||
"uniform vec4 Grid;\n"
|
||||
"uniform vec2 GridWidth;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec2 uv;\n"
|
||||
" vec2 texel = Frag_UV * TextureSize;\n"
|
||||
" if (ForceNearestSampling)\n"
|
||||
" uv = (floor(texel) + vec2(0.5,0.5)) / TextureSize;\n"
|
||||
" else\n"
|
||||
" uv = Frag_UV;\n"
|
||||
" vec2 texelEdge = step(mod(texel,vec2(1.0,1.0)),GridWidth);\n"
|
||||
" float isGrid = max(texelEdge.x, texelEdge.y);\n"
|
||||
" vec4 ct = ColorTransform * texture2D(Texture, uv) + ColorOffset;\n"
|
||||
" ct.rgb = ct.rgb * mix(1.0, ct.a, PremultiplyAlpha);\n"
|
||||
" ct.rgb += BackgroundColor * (1.0-ct.a);\n"
|
||||
" ct.a = mix(ct.a, 1.0, DisableFinalAlpha);\n"
|
||||
" ct = mix(ct, vec4(Grid.rgb,1), Grid.a * isGrid);\n"
|
||||
" gl_FragColor = ct;\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar *fragment_shader_glsl_130 = "uniform sampler2D Texture;\n"
|
||||
"uniform vec2 TextureSize;\n"
|
||||
"uniform mat4 ColorTransform;\n"
|
||||
"uniform vec4 ColorOffset;\n"
|
||||
"uniform vec3 BackgroundColor;\n"
|
||||
"uniform float PremultiplyAlpha;\n"
|
||||
"uniform float DisableFinalAlpha;\n"
|
||||
"uniform bool ForceNearestSampling;\n"
|
||||
"uniform vec4 Grid;\n"
|
||||
"uniform vec2 GridWidth;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec2 uv;\n"
|
||||
" vec2 texel = Frag_UV * TextureSize;\n"
|
||||
" if (ForceNearestSampling)\n"
|
||||
" uv = (floor(texel) + vec2(0.5,0.5)) / TextureSize;\n"
|
||||
" else\n"
|
||||
" uv = Frag_UV;\n"
|
||||
" vec2 texelEdge = step(mod(texel,vec2(1.0,1.0)),GridWidth);\n"
|
||||
" float isGrid = max(texelEdge.x, texelEdge.y);\n"
|
||||
" vec4 ct = ColorTransform * texture(Texture, uv) + ColorOffset;\n"
|
||||
" ct.rgb = ct.rgb * mix(1.0, ct.a, PremultiplyAlpha);\n"
|
||||
" ct.rgb += BackgroundColor * (1.0-ct.a);\n"
|
||||
" ct.a = mix(ct.a, 1.0, DisableFinalAlpha);\n"
|
||||
" ct = mix(ct, vec4(Grid.rgb,1), Grid.a * isGrid);\n"
|
||||
" Out_Color = ct;\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar *fragment_shader_glsl_300_es = "precision mediump float;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"uniform vec2 TextureSize;\n"
|
||||
"uniform mat4 ColorTransform;\n"
|
||||
"uniform vec4 ColorOffset;\n"
|
||||
"uniform vec3 BackgroundColor;\n"
|
||||
"uniform float PremultiplyAlpha;\n"
|
||||
"uniform float DisableFinalAlpha;\n"
|
||||
"uniform bool ForceNearestSampling;\n"
|
||||
"uniform vec4 Grid;\n"
|
||||
"uniform vec2 GridWidth;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec2 uv;\n"
|
||||
" vec2 texel = Frag_UV * TextureSize;\n"
|
||||
" if (ForceNearestSampling)\n"
|
||||
" uv = (floor(texel) + vec2(0.5,0.5)) / TextureSize;\n"
|
||||
" else\n"
|
||||
" uv = Frag_UV;\n"
|
||||
" vec2 texelEdge = step(mod(texel,vec2(1.0,1.0)),GridWidth);\n"
|
||||
" float isGrid = max(texelEdge.x, texelEdge.y);\n"
|
||||
" vec4 ct = ColorTransform * texture(Texture, uv) + ColorOffset;\n"
|
||||
" ct.rgb = ct.rgb * mix(1.0, ct.a, PremultiplyAlpha);\n"
|
||||
" ct.rgb += BackgroundColor * (1.0-ct.a);\n"
|
||||
" ct.a = mix(ct.a, 1.0, DisableFinalAlpha);\n"
|
||||
" ct = mix(ct, vec4(Grid.rgb,1), Grid.a * isGrid);\n"
|
||||
" Out_Color = ct;\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar *fragment_shader_glsl_410_core = "uniform sampler2D Texture;\n"
|
||||
"uniform vec2 TextureSize;\n"
|
||||
"uniform mat4 ColorTransform;\n"
|
||||
"uniform vec4 ColorOffset;\n"
|
||||
"uniform vec3 BackgroundColor;\n"
|
||||
"uniform float PremultiplyAlpha;\n"
|
||||
"uniform float DisableFinalAlpha;\n"
|
||||
"uniform bool ForceNearestSampling;\n"
|
||||
"uniform vec4 Grid;\n"
|
||||
"uniform vec2 GridWidth;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec2 uv;\n"
|
||||
" vec2 texel = Frag_UV * TextureSize;\n"
|
||||
" if (ForceNearestSampling)\n"
|
||||
" uv = (floor(texel) + vec2(0.5,0.5)) / TextureSize;\n"
|
||||
" else\n"
|
||||
" uv = Frag_UV;\n"
|
||||
" vec2 texelEdge = step(mod(texel,vec2(1.0,1.0)),GridWidth);\n"
|
||||
" float isGrid = max(texelEdge.x, texelEdge.y);\n"
|
||||
" vec4 ct = ColorTransform * texture(Texture, uv) + ColorOffset;\n"
|
||||
" ct.rgb = ct.rgb * mix(1.0, ct.a, PremultiplyAlpha);\n"
|
||||
" ct.rgb += BackgroundColor * (1.0-ct.a);\n"
|
||||
" ct.a = mix(ct.a, 1.0, DisableFinalAlpha);\n"
|
||||
" ct = mix(ct, vec4(Grid.rgb,1), Grid.a * isGrid);\n"
|
||||
" Out_Color = ct;\n"
|
||||
"}\n";
|
||||
|
||||
/* BuildShader is from imgui_impl_opengl3.cpp. Only change is to query the
|
||||
* additional uniform locations for the new fragment shader*/
|
||||
void BuildShader()
|
||||
{
|
||||
// Shader selection code based on imgui_impl_opengl3.cpp
|
||||
|
||||
// Parse GLSL version string
|
||||
int glsl_version = 130;
|
||||
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
|
||||
|
||||
// Select shaders matching our GLSL versions
|
||||
const GLchar *vertex_shader = NULL;
|
||||
const GLchar *fragment_shader = NULL;
|
||||
if (glsl_version < 130)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_120;
|
||||
fragment_shader = fragment_shader_glsl_120;
|
||||
}
|
||||
else if (glsl_version >= 410)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_410_core;
|
||||
fragment_shader = fragment_shader_glsl_410_core;
|
||||
}
|
||||
else if (glsl_version == 300)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_300_es;
|
||||
fragment_shader = fragment_shader_glsl_300_es;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_130;
|
||||
fragment_shader = fragment_shader_glsl_130;
|
||||
}
|
||||
|
||||
if (fragment_shader == NULL)
|
||||
{
|
||||
fprintf(stderr, "ERROR: imgui_tex_inspect fragment shader for %s not implemented yet", g_GlslVersionString);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create shaders
|
||||
const GLchar *vertex_shader_with_version[2] = {g_GlslVersionString, vertex_shader};
|
||||
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
|
||||
glCompileShader(g_VertHandle);
|
||||
CheckShader(g_VertHandle, "vertex shader");
|
||||
|
||||
const GLchar *fragment_shader_with_version[2] = {g_GlslVersionString, fragment_shader};
|
||||
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
|
||||
glCompileShader(g_FragHandle);
|
||||
CheckShader(g_FragHandle, "fragment shader");
|
||||
|
||||
g_ShaderHandle = glCreateProgram();
|
||||
glAttachShader(g_ShaderHandle, g_VertHandle);
|
||||
glAttachShader(g_ShaderHandle, g_FragHandle);
|
||||
glLinkProgram(g_ShaderHandle);
|
||||
CheckProgram(g_ShaderHandle, "shader program");
|
||||
|
||||
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
|
||||
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
|
||||
g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position");
|
||||
g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(g_ShaderHandle, "UV");
|
||||
|
||||
// Change from imgui_impl_opengl3.cpp (Our shader doesn't use vertex color)
|
||||
//g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(g_ShaderHandle, "Color");
|
||||
|
||||
// New uniforms used by imgui_tex_inspect
|
||||
g_UniformLocationTextureSize = glGetUniformLocation(g_ShaderHandle, "TextureSize");
|
||||
g_UniformLocationColorTransform = glGetUniformLocation(g_ShaderHandle, "ColorTransform");
|
||||
g_UniformLocationColorOffset = glGetUniformLocation(g_ShaderHandle, "ColorOffset");
|
||||
g_UniformLocationBackgroundColor = glGetUniformLocation(g_ShaderHandle, "BackgroundColor");
|
||||
g_UniformLocationPremultiplyAlpha = glGetUniformLocation(g_ShaderHandle, "PremultiplyAlpha");
|
||||
g_UniformLocationDisableFinalAlpha = glGetUniformLocation(g_ShaderHandle, "DisableFinalAlpha");
|
||||
g_UniformLocationGrid = glGetUniformLocation(g_ShaderHandle, "Grid");
|
||||
g_UniformLocationForceNearestSampling = glGetUniformLocation(g_ShaderHandle, "ForceNearestSampling");
|
||||
g_UniformLocationGridWidth = glGetUniformLocation(g_ShaderHandle, "GridWidth");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace imgui_impl_opengl
|
||||
|
||||
|
||||
namespace ImGuiTexInspect
|
||||
{
|
||||
using namespace imgui_impl_opengl;
|
||||
|
||||
static GLuint readbackFramebuffer = 0;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Init and Shutdown
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
bool ImplOpenGL3_Init(const char *glsl_version)
|
||||
{
|
||||
imgui_impl_opengl::ImGui_ImplOpenGL3_Init(glsl_version);
|
||||
BuildShader();
|
||||
glGenFramebuffers(1, &readbackFramebuffer);
|
||||
return true;
|
||||
}
|
||||
void ImplOpenGl3_Shutdown()
|
||||
{
|
||||
// No need to call ImGui_ImplOpenGL3_Shutdown, it doesn't even
|
||||
// exist in the imgui_impl_opengl namespace. Our version of
|
||||
// ImGui_ImplOpenGL3_Init doesn't affect OpenGL state.
|
||||
|
||||
glDeleteShader(g_VertHandle);
|
||||
glDeleteShader(g_FragHandle);
|
||||
glDeleteProgram(g_ShaderHandle);
|
||||
|
||||
g_VertHandle = 0;
|
||||
g_FragHandle = 0;
|
||||
g_ShaderHandle = 0;
|
||||
|
||||
glDeleteFramebuffers(1, &readbackFramebuffer);
|
||||
readbackFramebuffer = 0;
|
||||
}
|
||||
|
||||
void GiveNotInitializedWarning()
|
||||
{
|
||||
static bool warningGiven = false;
|
||||
if (!warningGiven)
|
||||
{
|
||||
fprintf(stderr, "ERROR: ImGuiTexInspect backend not initialized\n");
|
||||
warningGiven = true;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] BackEnd functions declared in imgui_tex_inspect_internal.h
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void BackEnd_SetShader(const ImDrawList *, const ImDrawCmd *, const Inspector *inspector)
|
||||
{
|
||||
const ShaderOptions *texConversion = &inspector->CachedShaderOptions;
|
||||
if (g_ShaderHandle)
|
||||
{
|
||||
ImDrawData *draw_data = ImGui::GetDrawData();
|
||||
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
||||
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
||||
|
||||
if (fb_width <= 0 || fb_height <= 0)
|
||||
return;
|
||||
|
||||
// Setup normal ImGui GL state
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height);
|
||||
|
||||
// Setup imgui_tex_inspect specific shader uniforms
|
||||
glUniformMatrix4fv(g_UniformLocationColorTransform, 1, GL_FALSE, texConversion->ColorTransform);
|
||||
glUniform2fv(g_UniformLocationTextureSize, 1, &inspector->TextureSize.x);
|
||||
glUniform4fv(g_UniformLocationColorOffset, 1, texConversion->ColorOffset);
|
||||
glUniform3fv(g_UniformLocationBackgroundColor, 1, &texConversion->BackgroundColor.x);
|
||||
glUniform1f(g_UniformLocationPremultiplyAlpha, texConversion->PremultiplyAlpha);
|
||||
glUniform1f(g_UniformLocationDisableFinalAlpha, texConversion->DisableFinalAlpha);
|
||||
glUniform1i(g_UniformLocationForceNearestSampling, texConversion->ForceNearestSampling);
|
||||
glUniform2fv(g_UniformLocationGridWidth, 1, &texConversion->GridWidth.x);
|
||||
glUniform4fv(g_UniformLocationGrid, 1, &texConversion->GridColor.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
GiveNotInitializedWarning();
|
||||
}
|
||||
}
|
||||
bool BackEnd_GetData(Inspector *inspector, ImTextureID texture, int /*x*/, int /*y*/, int /*width*/, int /*height*/, BufferDesc *bufferDesc)
|
||||
{
|
||||
// Current simple implementation just gets data for whole texture
|
||||
|
||||
if (readbackFramebuffer == 0)
|
||||
{
|
||||
GiveNotInitializedWarning();
|
||||
return false;
|
||||
}
|
||||
const int numChannels = 4;
|
||||
glGetError(); // Discard any current error so that check at end of function is useful
|
||||
void * data;
|
||||
int texWidth = (int)inspector->TextureSize.x;
|
||||
int texHeight = (int)inspector->TextureSize.y;
|
||||
GLuint glTexture = (GLuint)(uintptr_t)texture; //Double cast to avoid warning
|
||||
|
||||
#ifdef IMGUI_TEX_INSPECT_FLOAT_READ_ENABLED
|
||||
size_t bufferSize = sizeof(float) * texWidth * texHeight * numChannels;
|
||||
bufferDesc->Data_float = (float *)GetBuffer(inspector, bufferSize);
|
||||
GLuint type = GL_FLOAT;
|
||||
data = (void *)bufferDesc->Data_float;
|
||||
#else
|
||||
size_t bufferSize = sizeof(uint8_t) * texWidth * texHeight * numChannels;
|
||||
bufferDesc->Data_uint8_t = (uint8_t *)GetBuffer(inspector, bufferSize);
|
||||
GLuint type = GL_UNSIGNED_BYTE;
|
||||
data = (void *)bufferDesc->Data_uint8_t;
|
||||
#endif
|
||||
|
||||
if (data == NULL)
|
||||
return false;
|
||||
|
||||
bufferDesc->BufferByteSize = bufferSize;
|
||||
bufferDesc->Red = 0; // Data is packed such that red channel is first
|
||||
bufferDesc->Green = 1; // then green, then blue, the alpha. Hence, 0,1,2,3
|
||||
bufferDesc->Blue = 2; // for these channel order values.
|
||||
bufferDesc->Alpha = 3;
|
||||
bufferDesc->ChannelCount = 4; // RGBA
|
||||
bufferDesc->LineStride = (int)inspector->TextureSize.x * numChannels;
|
||||
bufferDesc->Stride = 4; // No padding between each RGBA texel
|
||||
bufferDesc->StartX = 0; // We queried the whole texture
|
||||
bufferDesc->StartY = 0;
|
||||
bufferDesc->Width = texWidth;
|
||||
bufferDesc->Height = texHeight;
|
||||
|
||||
// Save current frame buffer so we can restore it
|
||||
GLuint currentFramebuffer;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)¤tFramebuffer);
|
||||
|
||||
// Read texture data
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, readbackFramebuffer);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glTexture, 0);
|
||||
glReadPixels(0, 0, texWidth, texHeight, GL_RGBA, type, data);
|
||||
|
||||
// Restore previous framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, currentFramebuffer);
|
||||
|
||||
return glGetError() == GL_NO_ERROR;
|
||||
}
|
||||
} // namespace ImGuiTexInspect
|
9
tex_inspect_opengl.h
Normal file
9
tex_inspect_opengl.h
Normal file
@ -0,0 +1,9 @@
|
||||
// ImGuiTexInspect, a texture inspector widget for dear imgui
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
namespace ImGuiTexInspect
|
||||
{
|
||||
bool ImplOpenGL3_Init(const char *glsl_version = NULL);
|
||||
void ImplOpenGl3_Shutdown();
|
||||
} // namespace ImGuiTexInspect
|
Loading…
x
Reference in New Issue
Block a user