initial commit

This commit is contained in:
Tanishq Dubey 2025-04-07 20:08:16 -04:00
commit 7e7207a87e
50 changed files with 86524 additions and 0 deletions

43
.gitignore vendored Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

918
exif.cpp Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

22948
imgui.cpp Normal file

File diff suppressed because it is too large Load Diff

4033
imgui.h Normal file

File diff suppressed because it is too large Load Diff

270
imgui.ini Normal file
View 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

File diff suppressed because it is too large Load Diff

4857
imgui_draw.cpp Normal file

File diff suppressed because it is too large Load Diff

1023
imgui_impl_opengl3.cpp Normal file

File diff suppressed because it is too large Load Diff

67
imgui_impl_opengl3.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

50
imgui_impl_sdl2.h Normal file
View 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

File diff suppressed because it is too large Load Diff

50
imgui_impl_sdl3.h Normal file
View 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

File diff suppressed because it is too large Load Diff

4525
imgui_tables.cpp Normal file

File diff suppressed because it is too large Load Diff

1184
imgui_tex_inspect.cpp Normal file

File diff suppressed because it is too large Load Diff

308
imgui_tex_inspect.h Normal file
View 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

View 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

File diff suppressed because it is too large Load Diff

627
imstb_rectpack.h Normal file
View 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

File diff suppressed because it is too large Load Diff

5085
imstb_truetype.h Normal file

File diff suppressed because it is too large Load Diff

2112
llm.md Normal file

File diff suppressed because it is too large Load Diff

2280
main.cpp Normal file

File diff suppressed because it is too large Load Diff

58
shaders/clarity.frag Normal file
View 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
View 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
View 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
View 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);
}

View 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
View 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
}

View 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
View 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
View 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
View 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);
}

View 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
View 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
View 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
View 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);
}

View 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
View 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
View 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, &current_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*)&current_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 *)&currentFramebuffer);
// 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
View 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