diff --git a/Makefile b/Makefile index fc45bd6..7dbff72 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ LINUX_GL_LIBS = -lGL CXXFLAGS = -std=c++20 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends CXXFLAGS += -DIMGUI_DEFINE_MATH_OPERATORS -Ofast -LIBS = +LIBS = -lexiv2 ##--------------------------------------------------------------------- ## OPENGL ES @@ -44,9 +44,9 @@ LIBS = ifeq ($(UNAME_S), Linux) #LINUX ECHO_MESSAGE = "Linux" - LIBS += $(LINUX_GL_LIBS) -ldl `sdl2-config --libs` + LIBS += $(LINUX_GL_LIBS) -ldl -L/usr/lib -lSDL2 - CXXFLAGS += `sdl2-config --cflags` + CXXFLAGS += -I/usr/include/SDL2 -D_REENTRANT CFLAGS = $(CXXFLAGS) endif diff --git a/main.cpp b/main.cpp index 25cca24..eb1c881 100644 --- a/main.cpp +++ b/main.cpp @@ -7,6 +7,7 @@ #include "lib/imgui.h" #include "lib/imgui_internal.h" #include +#include #include #include #include @@ -34,50 +35,139 @@ #include "lib/backends/tex_inspect_opengl.h" #include "lib/argparse.hpp" +#include + struct Args : public argparse::Args { std::string &fpath = arg("path to the image"); }; + +struct EXIFData { + std::string CameraMake; + std::string CameraModel; + std::string LensModel; + std::string ImageOrientation; + std::string ImageDimensionX; + std::string ImageDimensiony; + std::string ISO; + std::string ShutterSpeed; + std::string FNumber; + std::string FocalLength; + std::string ExposureBias; + std::string MeteringMode; + std::string Flash; + std::string Time; + std::string TimeTaken; + std::string TimeTakenOffset; + std::string GPSLat; + std::string GPSLatref; + std::string GPSLon; + std::string GPSLonref; +}; + struct Texture { ImTextureID texture; ImVec2 size; int channels; + EXIFData exif; }; static uint8_t* image = nullptr; -Texture LoadTexture(const char * path) -{ - const int channelCount = 4; - int imageFileChannelCount; - int width, height; - image = (uint8_t *)stbi_load(path, &width, &height, &imageFileChannelCount, channelCount); - if (image == NULL) - { - fprintf(stderr, "%s\nFailed to open %s\n", stbi_failure_reason(), path); - return {nullptr,{0,0}}; +EXIFData printExifData(const std::string& imagePath) { + EXIFData d; + d.CameraMake = "NULL"; + try { + // Load the image + Exiv2::Image::UniquePtr img = Exiv2::ImageFactory::open(imagePath); + if (img.get() == 0) { + std::cerr << "Error: Could not open image file " << imagePath << std::endl; + return d; + } + img->readMetadata(); + + // Get the Exif data + Exiv2::ExifData &exifData = img->exifData(); + if (exifData.empty()) { + std::cerr << "No EXIF data found in file " << imagePath << std::endl; + return d; + } + + // Helper function to print EXIF data if available + auto printExifValue = [&exifData](const char* key, std::string *d) { + Exiv2::ExifKey exifKey(key); + Exiv2::ExifData::const_iterator pos = exifData.findKey(exifKey); + if (pos != exifData.end()) { + *d = pos->value().toString(); + } + }; + + // Print common EXIF data + printExifValue("Exif.Image.Make", &d.CameraMake); + printExifValue("Exif.Image.Model", &d.CameraModel); + printExifValue("Exif.Photo.LensModel", &d.LensModel); + + printExifValue("Exif.Image.Orientation", &d.ImageOrientation); + // + printExifValue("Exif.Photo.ExposureTime", &d.ShutterSpeed); + printExifValue("Exif.Photo.FNumber", &d.FNumber); + printExifValue("Exif.Photo.ISOSpeedRatings", &d.ISO); + printExifValue("Exif.Photo.FocalLength", &d.FocalLength); + printExifValue("Exif.Photo.ExposureBiasValue", &d.ExposureBias); + printExifValue("Exif.Photo.MeteringMode", &d.MeteringMode); + printExifValue("Exif.Photo.Flash", &d.Flash); + + printExifValue("Exif.Image.DateTime", &d.Time); + printExifValue("Exif.Photo.DateTimeOriginal", &d.TimeTaken); + printExifValue("Exif.Photo.OffsetTime", &d.TimeTakenOffset); + printExifValue("Exif.Photo.PixelXDimension", &d.ImageDimensionX); + printExifValue("Exif.Photo.PixelYDimension", &d.ImageDimensiony); + + printExifValue("Exif.GPSInfo.GPSLatitudeRef", &d.GPSLatref); + printExifValue("Exif.GPSInfo.GPSLatitude", &d.GPSLat); + printExifValue("Exif.GPSInfo.GPSLongitudeRef", &d.GPSLonref); + printExifValue("Exif.GPSInfo.GPSLongitude", &d.GPSLon); + + +/* +Exif.Image.Make 0x010f Ascii 5 SONY +Exif.Image.Model 0x0110 Ascii 10 ILCE-7RM5 + +Exif.Image.Orientation 0x0112 Short 1 8 +Exif.Photo.PixelXDimension 0xa002 Long 1 9504 +Exif.Photo.PixelYDimension 0xa003 Long 1 6336 + +Exif.Photo.ExposureTime 0x829a Rational 1 1/400 +Exif.Photo.FNumber 0x829d Rational 1 56/10 +Exif.Photo.ISOSpeedRatings 0x8827 Short 1 100 +Exif.Photo.FocalLength 0x920a Rational 1 400/10 +Exif.Photo.ExposureBiasValue 0x9204 SRational 1 -10/10 +Exif.Photo.MeteringMode 0x9207 Short 1 5 +Exif.Photo.Flash 0x9209 Short 1 16 + +Exif.Image.DateTime 0x0132 Ascii 20 2024:06:07 08:58:07 +Exif.Photo.DateTimeOriginal 0x9003 Ascii 20 2024:06:07 08:58:07 +Exif.Photo.OffsetTime 0x9010 Ascii 7 -05:00 + +Exif.GPSInfo.GPSLatitudeRef 0x0001 Ascii 2 N +Exif.GPSInfo.GPSLatitude 0x0002 Rational 3 40/1 43/1 32231/1000 +Exif.GPSInfo.GPSLongitudeRef 0x0003 Ascii 2 W +Exif.GPSInfo.GPSLongitude 0x0004 Rational 3 73/1 59/1 16688/1000 +*/ + + } catch (Exiv2::Error& e) { + std::cerr << "Error: " << e.what() << std::endl; } - GLenum dataFormat = GL_RGBA; - GLuint textureHandle; - glGenTextures(1, &textureHandle); - glBindTexture(GL_TEXTURE_2D, textureHandle); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, dataFormat, GL_UNSIGNED_BYTE, image); - - Texture t; - t.texture = (void*)(uintptr_t)(textureHandle); - t.size = ImVec2((float)width,(float)height); - t.channels = channelCount; - - return t; + return d; } -Texture ReloadTexture(ImTextureID id, int width, int height) { - GLuint tid = (GLuint)(uintptr_t)id; + + +Texture ReloadTexture(Texture tin, int width, int height) { + GLuint tid = (GLuint)(uintptr_t)tin.texture; glDeleteTextures((GLsizei)1, &tid); GLenum dataFormat = GL_RGBA; @@ -91,6 +181,8 @@ Texture ReloadTexture(ImTextureID id, int width, int height) { Texture t; t.texture = (void*)(uintptr_t)(textureHandle); t.size = ImVec2((float)width,(float)height); + t.exif = tin.exif; + t.channels = tin.channels; return t; } @@ -120,6 +212,43 @@ void RotateImage(Texture t) { delete[] tempBuffer; } +Texture LoadTexture(const char * path) +{ + const int channelCount = 4; + int imageFileChannelCount; + int width, height; + image = (uint8_t *)stbi_load(path, &width, &height, &imageFileChannelCount, channelCount); + + if (image == NULL) + { + fprintf(stderr, "%s\nFailed to open %s\n", stbi_failure_reason(), path); + + return {nullptr,{0,0}}; + } + + auto exif = printExifData(path); + + + + GLenum dataFormat = GL_RGBA; + GLuint textureHandle; + glGenTextures(1, &textureHandle); + glBindTexture(GL_TEXTURE_2D, textureHandle); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, dataFormat, GL_UNSIGNED_BYTE, image); + + Texture t; + t.texture = (void*)(uintptr_t)(textureHandle); + t.size = ImVec2((float)width,(float)height); + t.channels = channelCount; + t.exif = exif; + + + return t; +} + + const int MAX_ANNOATED_TEXELS = 10000; @@ -136,6 +265,7 @@ int main(int argc, char* argv[]) { bool GRID_ENABLED = false; bool AA_ENABLED = true; bool SHOW_HELP = false; + bool SHOW_EXIF = false; int MODE = 0; // Decide GL+GLSL versions @@ -252,11 +382,14 @@ int main(int argc, char* argv[]) { break; case SDL_SCANCODE_R: RotateImage(t); - t = ReloadTexture(t.texture, t.size.y, t.size.x); + t = ReloadTexture(t, t.size.y, t.size.x); break; case SDL_SCANCODE_Q: done = true; break; + case SDL_SCANCODE_E: + SHOW_EXIF = !SHOW_EXIF; + break; default: break; } @@ -284,7 +417,7 @@ int main(int argc, char* argv[]) { ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); ImGui::Begin("Main", NULL, - ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize); + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBringToFrontOnFocus); auto wSize = ImGui::GetContentRegionAvail(); if (t.texture != 0) { @@ -364,11 +497,132 @@ int main(int argc, char* argv[]) { ImGui::Text("\tFloat Values"); ImGui::Separator(); ImGui::Text("h - show help popup"); + ImGui::Text("e - toggle EXIF info"); + ImGui::Separator(); ImGui::Text("q - quit"); ImGui::Separator(); ImGui::Text("click anywhere to continue"); ImGui::EndPopup(); } + if (SHOW_EXIF && t.exif.CameraMake != "NULL") { + ImGuiWindowClass topmost; + topmost.ClassId = ImHashStr("TopMost"); + topmost.ViewportFlagsOverrideSet = ImGuiViewportFlags_TopMost; + ImGui::SetNextWindowClass(&topmost); + ImGui::Begin("EXIF", NULL, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing); + if(ImGui::BeginTable("Hardware", 2)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Make"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.CameraMake.c_str()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Model"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.CameraModel.c_str()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Lens"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.LensModel.c_str()); + + ImGui::EndTable(); + } + + ImGui::Separator(); + + if(ImGui::BeginTable("Photo", 2)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Shutter Speed"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.ShutterSpeed.c_str()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("F-Stop"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.FNumber.c_str()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("ISO"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.ISO.c_str()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Focal Length"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.FocalLength.c_str()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Exposure Comp"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.ExposureBias.c_str()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Metering Mode"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.MeteringMode.c_str()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Flash"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", t.exif.Flash.c_str()); + + ImGui::EndTable(); + } + + ImGui::Separator(); + + if(ImGui::BeginTable("Meta", 2)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Date"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s %s", t.exif.TimeTaken.c_str(), t.exif.TimeTakenOffset.c_str()); + + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Dimensions"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sx%s", t.exif.ImageDimensionX.c_str(), t.exif.ImageDimensiony.c_str()); + + ImGui::EndTable(); + } + + ImGui::Separator(); + + if(ImGui::BeginTable("Location", 2)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Latitude"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s %s", t.exif.GPSLat.c_str(), t.exif.GPSLatref.c_str()); + + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Longitude"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s %s", t.exif.GPSLon.c_str(), t.exif.GPSLonref.c_str()); + + ImGui::EndTable(); + } + + ImGui::Separator(); + ImGui::Text("Press e to hide"); + + ImGui::End(); + } } // Rendering