diff --git a/tests/g2chromatic2.cpp b/tests/g2chromatic2.cpp index 229931d..073d27f 100644 --- a/tests/g2chromatic2.cpp +++ b/tests/g2chromatic2.cpp @@ -9,7 +9,12 @@ #include "../util/output/aviwriter.hpp" #include "../util/output/bmpwriter.hpp" #include "../util/timing_decorator.cpp" + #include "../imgui/imgui.h" +#include "../imgui/backends/imgui_impl_glfw.h" +#include "../imgui/backends/imgui_impl_opengl3.h" +#include + struct AnimationConfig { int width = 1024; @@ -206,37 +211,62 @@ void mainLogic(){ } int main() { - static bool window = true; - ImGui::SetNextWindowSize(ImVec2(1110,667)); - auto beg = ImGui::Begin("Gradient thing", &window); - if (beg) { + //static bool window = true; + if (!glfwInit()) { + std::cerr << "gui stuff is dumb in c++." << std::endl; + glfwTerminate(); + return 1; + } + //ImGui::SetNextWindowSize(ImVec2(1110,667)); + //auto beg = ImGui::Begin("Gradient thing", &window); + //if (beg) { + std::cout << "stuff breaks at 220" << std::endl; + bool application_not_closed = true; + //IMGUI_CHECKVERSION(); //this was listed in imgui docs to add. dont know version strings to put here yet. + ImGui::CreateContext(); + std::cout << "context created" << std::endl; + ImGuiIO& io = ImGui::GetIO(); + std::cout << "io init?" << std::endl; + //float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); + GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "Dear ImGui GLFW+OpenGL3 example", nullptr, nullptr); + std::cout << "created glfw window" << std::endl; + + ImGui_ImplGlfw_InitForOpenGL(window, true); // Second param install_callback=true will install GLFW callbacks and chain to existing ones. + ImGui_ImplOpenGL3_Init(); + + while (true) { + std::cout << "stuff breaks at 220" << std::endl; ImGui::SetCursorPos(ImVec2(435.5,200)); ImGui::PushItemWidth(200); static int i1 = 123; ImGui::InputInt("Width", &i1); ImGui::PopItemWidth(); + std::cout << "stuff breaks at 227" << std::endl; ImGui::SetCursorPos(ImVec2(432,166)); ImGui::PushItemWidth(200); static int i2 = 123; ImGui::InputInt("Height", &i2); ImGui::PopItemWidth(); + std::cout << "stuff breaks at 234" << std::endl; ImGui::SetCursorPos(ImVec2(533.5,271)); ImGui::Button("Start", ImVec2(43,19)); //remove size argument (ImVec2) to auto-resize + std::cout << "stuff breaks at 238" << std::endl; ImGui::SetCursorPos(ImVec2(400.5,366)); ImGui::PushItemWidth(200); static int i6 = 123; ImGui::InputInt("number of Seeds", &i6); ImGui::PopItemWidth(); + std::cout << "stuff breaks at 245" << std::endl; } - ImGui::End(); + //ImGui::End(); FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED); return 0; } //I need this: https://raais.github.io/ImStudio/ // or this: https://github.com/tpecholt/imrad/ -// g++ -std=c++23 -O3 -march=native -o ./bin/g2gradc ./tests/g2chromatic2.cpp -I./imgui -L./imgui -limgui `pkg-config --cflags --libs glfw3` && ./bin/g2gradc \ No newline at end of file +// g++ -std=c++23 -O3 -march=native -o ./bin/g2gradc ./tests/g2chromatic2.cpp -I./imgui -L./imgui -limgui -lstb `pkg-config --cflags --libs glfw3` && ./bin/g2gradc \ No newline at end of file diff --git a/tests/spritegen.cpp b/tests/spritegen.cpp new file mode 100644 index 0000000..c38d5b0 --- /dev/null +++ b/tests/spritegen.cpp @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../util/grid/grid2.hpp" +#include "../util/output/aviwriter.hpp" +#include "../util/output/bmpwriter.hpp" +#include "../util/timing_decorator.cpp" +#include "../imgui/imgui.h" + +struct AnimationConfig { + int width = 1024; + int height = 1024; + int totalFrames = 480; + float fps = 30.0f; + int numSeeds = 8; +}; + +Grid2 setup(AnimationConfig config) { + TIME_FUNCTION; + Grid2 grid; + std::vector pos; + std::vector colors; + std::vector sizes; + for (int y = 0; y < config.height; ++y) { + for (int x = 0; x < config.width; ++x) { + float gradient = (x + y) / float(config.width + config.height - 2); + pos.push_back(Vec2(x,y)); + colors.push_back(Vec4(gradient, gradient, gradient, 1.0f)); + sizes.push_back(1.0f); + } + } + grid.bulkAddObjects(pos,colors,sizes); + return grid; +} + +void Preview(Grid2 grid) { + TIME_FUNCTION; + int width; + int height; + //std::vector rgbData; + + frame rgbData = grid.getGridAsFrame(frame::colormap::RGB); + bool success = BMPWriter::saveBMP("output/grayscalesource.bmp", rgbData); + if (!success) { + std::cout << "yo! this failed in Preview" << std::endl; + } +} + +std::vector> pickSeeds(Grid2 grid, AnimationConfig config) { + TIME_FUNCTION; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> xDist(0, config.width - 1); + std::uniform_int_distribution<> yDist(0, config.height - 1); + std::uniform_real_distribution<> colorDist(0.2f, 0.8f); + + std::vector> seeds; + + for (int i = 0; i < config.numSeeds; ++i) { + Vec2 point(xDist(gen), yDist(gen)); + Vec4 color(colorDist(gen), colorDist(gen), colorDist(gen), 255); + size_t id = grid.getPositionVec(point); + grid.setColor(id, color); + seeds.push_back(std::make_tuple(id,point, color)); + } + return seeds; +} + +void expandPixel(Grid2& grid, AnimationConfig config, std::vector>& seeds) { + TIME_FUNCTION; + std::vector> newseeds; + + std::unordered_set visitedThisFrame; + for (const auto& seed : seeds) { + visitedThisFrame.insert(std::get<0>(seed)); + } + + //#pragma omp parallel for + for (const std::tuple& seed : seeds) { + size_t id = std::get<0>(seed); + Vec2 seedPOS = std::get<1>(seed); + Vec4 seedColor = std::get<2>(seed); + std::vector neighbors = grid.getNeighbors(id); + //grid.setSize(id, grid.getSize(id)+4); + for (size_t neighbor : neighbors) { + if (visitedThisFrame.count(neighbor)) { + continue; + } + visitedThisFrame.insert(neighbor); + + Vec2 neipos = grid.getPositionID(neighbor); + Vec4 neighborColor = grid.getColor(neighbor); + float distance = seedPOS.distance(neipos); + float angle = seedPOS.directionTo(neipos); + + float normalizedAngle = (angle + M_PI) / (2.0f * M_PI); + float blendFactor = 0.3f + 0.4f * std::sin(normalizedAngle * 2.0f * M_PI); + blendFactor = std::clamp(blendFactor, 0.1f, 0.9f); + + Vec4 newcolor = Vec4( + seedColor.r * blendFactor + neighborColor.r * (1.0f - blendFactor), + seedColor.g * (1.0f - blendFactor) + neighborColor.g * blendFactor, + seedColor.b * (0.5f + 0.5f * std::sin(normalizedAngle * 4.0f * M_PI)), + 1.0f + ); + + newcolor = newcolor.clamp(0.0f, 1.0f); + + grid.setColor(neighbor, newcolor); + newseeds.emplace_back(neighbor, neipos, newcolor); + } + } + seeds.clear(); + seeds.shrink_to_fit(); + seeds = std::move(newseeds); +} + +//bool exportavi(std::vector> frames, AnimationConfig config) { +bool exportavi(std::vector frames, AnimationConfig config) { + TIME_FUNCTION; + std::string filename = "output/chromatic_transformation.avi"; + + std::cout << "Frame count: " << frames.size() << std::endl; + + // Log compression statistics for all frames + std::cout << "\n=== Frame Compression Statistics ===" << std::endl; + size_t totalOriginalSize = 0; + size_t totalCompressedSize = 0; + + for (int i = 0; i < frames.size(); ++i) { + totalOriginalSize += frames[i].getSourceSize(); + totalCompressedSize += frames[i].getTotalCompressedSize(); + } + + double overallRatio = static_cast(totalOriginalSize) / totalCompressedSize; + double overallSavings = (1.0 - 1.0/overallRatio) * 100.0; + + std::cout << "\n=== Overall Compression Summary ===" << std::endl; + std::cout << "Total frames: " << frames.size() << std::endl; + std::cout << "Compressed frames: " << frames.size() << std::endl; + std::cout << "Total original size: " << totalOriginalSize << " bytes (" + << std::fixed << std::setprecision(2) << (totalOriginalSize / (1024.0 * 1024.0)) << " MB)" << std::endl; + std::cout << "Total compressed size: " << totalCompressedSize << " bytes (" + << std::fixed << std::setprecision(2) << (totalCompressedSize / (1024.0 * 1024.0)) << " MB)" << std::endl; + std::cout << "Overall compression ratio: " << std::fixed << std::setprecision(2) << overallRatio << ":1" << std::endl; + std::cout << "Overall space savings: " << std::fixed << std::setprecision(1) << overallSavings << "%" << std::endl; + + std::filesystem::path dir = "output"; + if (!std::filesystem::exists(dir)) { + if (!std::filesystem::create_directories(dir)) { + std::cout << "Failed to create output directory!" << std::endl; + return false; + } + } + + bool success = AVIWriter::saveAVIFromCompressedFrames(filename, frames, frames[0].getWidth(), frames[0].getHeight(), config.fps); + + if (success) { + // Check if file actually exists + if (std::filesystem::exists(filename)) { + auto file_size = std::filesystem::file_size(filename); + std::cout << "\nAVI file created successfully: " << filename + << " (" << file_size << " bytes, " + << std::fixed << std::setprecision(2) << (file_size / (1024.0 * 1024.0)) << " MB)" << std::endl; + } + } else { + std::cout << "Failed to save AVI file!" << std::endl; + } + + return success; +} + +void mainLogic(){ + AnimationConfig config; + // std::cout << "g2c2175" << std::endl; + + Grid2 grid = setup(config); + // std::cout << "g2c2178" << std::endl; + Preview(grid); + std::vector> seeds = pickSeeds(grid,config); + std::vector frames; + + for (int i = 0; i < config.totalFrames; ++i){ + expandPixel(grid,config,seeds); + + // Print compression info for this frame + if (i % 10 == 0 ) { + frame bgrframe; + std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl; + bgrframe = grid.getGridAsFrame(frame::colormap::BGR); + bgrframe.printCompressionStats(); + //(bgrframe, i + 1); + frames.push_back(bgrframe); + //bgrframe.decompress(); + //BMPWriter::saveBMP(std::format("output/grayscalesource.{}.bmp", i), bgrframe); + bgrframe.compressFrameLZ78(); + } + + } + + exportavi(frames,config); +} + +int main() { + static bool window = true; + ImGui::SetNextWindowSize(ImVec2(1110,667)); + auto beg = ImGui::Begin("Gradient thing", &window); + if (beg) { + + ImGui::SetCursorPos(ImVec2(435.5,200)); + ImGui::PushItemWidth(200); + static int i1 = 123; + ImGui::InputInt("Width", &i1); + ImGui::PopItemWidth(); + + ImGui::SetCursorPos(ImVec2(432,166)); + ImGui::PushItemWidth(200); + static int i2 = 123; + ImGui::InputInt("Height", &i2); + ImGui::PopItemWidth(); + + ImGui::SetCursorPos(ImVec2(533.5,271)); + ImGui::Button("Start", ImVec2(43,19)); //remove size argument (ImVec2) to auto-resize + + ImGui::SetCursorPos(ImVec2(400.5,366)); + ImGui::PushItemWidth(200); + static int i6 = 123; + ImGui::InputInt("number of Seeds", &i6); + ImGui::PopItemWidth(); + + } + ImGui::End(); + FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED); + return 0; +} \ No newline at end of file diff --git a/util/grid/sprite2.hpp b/util/grid/sprite2.hpp index d6560a1..a22c928 100644 --- a/util/grid/sprite2.hpp +++ b/util/grid/sprite2.hpp @@ -6,20 +6,20 @@ class SpriteMap2 : public Grid2 { private: - // id, sprite std::unordered_map spritesComped; std::unordered_map Layers; std::unordered_map Orientations; public: - using Grid2::Grid2; + size_t addSprite(const Vec2& pos, frame sprite, int layer = 0, float orientation = 0.0f) { size_t id = addObject(pos, Vec4(0,0,0,0)); spritesComped[id] = sprite; Layers[id] = layer; Orientations[id] = orientation; + return id; } frame getSprite(size_t id) { @@ -42,13 +42,15 @@ public: float getOrientation(size_t id) { return Orientations.at(id); } + size_t setOrientation(size_t id, float orientation) { Orientations[id] = orientation; + return id; } void getGridRegionAsBGR(const Vec2& minCorner, const Vec2& maxCorner, int& width, int& height, std::vector& rgbData) const { TIME_FUNCTION; - // std::cout << "excessdebug g2.483" << std::endl; + // Calculate dimensions width = static_cast(maxCorner.x - minCorner.x); height = static_cast(maxCorner.y - minCorner.y); @@ -59,17 +61,105 @@ public: rgbData.shrink_to_fit(); return; } - // std::cout << "excessdebug g2.494" << std::endl; - // Initialize RGB data (3 bytes per pixel: R, G, B) + // Initialize RGBA buffer for compositing std::vector rgbaBuffer(width * height, Vec4(0.0f, 0.0f, 0.0f, 0.0f)); - // For each position in the grid, find the corresponding pixel + // Group sprites by layer for proper rendering order + std::vector> layeredSprites; for (const auto& [id, pos] : Positions) { - // std::cout << "excessdebug g2.501." << id << std::endl; + if (spritesComped.find(id) != spritesComped.end()) { + layeredSprites.emplace_back(Layers.at(id), id); + } + } + + // Sort by layer (lower layers first) + std::sort(layeredSprites.begin(), layeredSprites.end(), + [](const auto& a, const auto& b) { return a.first < b.first; }); + + // Render each sprite in layer order + for (const auto& [layer, id] : layeredSprites) { + const Vec2& pos = Positions.at(id); + const frame& sprite = spritesComped.at(id); + float orientation = Orientations.at(id); + + // Decompress sprite if needed + frame decompressedSprite = sprite; + if (sprite.isCompressed()) { + decompressedSprite.decompress(); + } + + const std::vector& spriteData = decompressedSprite.getData(); + size_t spriteWidth = decompressedSprite.getWidth(); + size_t spriteHeight = decompressedSprite.getHeight(); + + if (spriteData.empty() || spriteWidth == 0 || spriteHeight == 0) { + continue; + } + + // Calculate sprite bounds in world coordinates + float halfWidth = spriteWidth / 2.0f; + float halfHeight = spriteHeight / 2.0f; + + // Apply rotation if needed + // TODO: Implement proper rotation transformation + int pixelXm = static_cast(pos.x - halfWidth - minCorner.x); + int pixelXM = static_cast(pos.x + halfWidth - minCorner.x); + int pixelYm = static_cast(pos.y - halfHeight - minCorner.y); + int pixelYM = static_cast(pos.y + halfHeight - minCorner.y); + + // Clamp to output bounds + pixelXm = std::max(0, pixelXm); + pixelXM = std::min(width - 1, pixelXM); + pixelYm = std::max(0, pixelYm); + pixelYM = std::min(height - 1, pixelYM); + + // Skip if completely outside bounds + if (pixelXm >= width || pixelXM < 0 || pixelYm >= height || pixelYM < 0) { + continue; + } + + // Render sprite pixels + for (int py = pixelYm; py <= pixelYM; ++py) { + for (int px = pixelXm; px <= pixelXM; ++px) { + // Calculate sprite-relative coordinates + int spriteX = px - pixelXm; + int spriteY = py - pixelYm; + + // Skip if outside sprite bounds + if (spriteX < 0 || spriteX >= spriteWidth || spriteY < 0 || spriteY >= spriteHeight) { + continue; + } + + // Get sprite pixel color based on color format + Vec4 spriteColor = getSpritePixelColor(spriteData, spriteX, spriteY, spriteWidth, spriteHeight, decompressedSprite.colorFormat); + + // Alpha blending + int bufferIndex = py * width + px; + Vec4& dest = rgbaBuffer[bufferIndex]; + + float srcAlpha = spriteColor.a; + if (srcAlpha > 0.0f) { + float invSrcAlpha = 1.0f - srcAlpha; + dest.r = spriteColor.r * srcAlpha + dest.r * invSrcAlpha; + dest.g = spriteColor.g * srcAlpha + dest.g * invSrcAlpha; + dest.b = spriteColor.b * srcAlpha + dest.b * invSrcAlpha; + dest.a = srcAlpha + dest.a * invSrcAlpha; + } + } + } + } + + // Also render regular colored objects (from base class) + for (const auto& [id, pos] : Positions) { + // Skip if this is a sprite (already rendered above) + if (spritesComped.find(id) != spritesComped.end()) { + continue; + } + size_t size = Sizes.at(id); - - // Calculate pixel coordinates + + // Calculate pixel coordinates for colored objects int pixelXm = static_cast(pos.x - size/2 - minCorner.x); int pixelXM = static_cast(pos.x + size/2 - minCorner.x); int pixelYm = static_cast(pos.y - size/2 - minCorner.y); @@ -79,42 +169,36 @@ public: pixelXM = std::min(width - 1, pixelXM); pixelYm = std::max(0, pixelYm); pixelYM = std::min(height - 1, pixelYM); - // std::cout << "excessdebug g2.514." << id << std::endl; - + // Ensure within bounds if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) { - // std::cout << "excessdebug g2.518." << id << " - (" << pixelXm << "," << pixelYM << ")" << std::endl; const Vec4& color = Colors.at(id); float srcAlpha = color.a; - float invSrcAlpha = 1.0f - srcAlpha; - for (int py = pixelYm; py <= pixelYM; ++py){ - for (int px = pixelXm; px <= pixelXM; ++px){ - // std::cout << "excessdebug g2.524." << id << " - (" << py << "," << px << ")" << std::endl; - int index = (py * width + px); - Vec4 dest = rgbaBuffer[index]; + for (int py = pixelYm; py <= pixelYM; ++py) { + for (int px = pixelXm; px <= pixelXM; ++px) { + int index = py * width + px; + Vec4& dest = rgbaBuffer[index]; - dest.r = color.r * srcAlpha + dest.r; // * invSrcAlpha; - dest.g = color.g * srcAlpha + dest.g; // * invSrcAlpha; - dest.b = color.b * srcAlpha + dest.b; // * invSrcAlpha; - dest.a = srcAlpha + dest.a; // * invSrcAlpha; - rgbaBuffer[index] = dest; + float invSrcAlpha = 1.0f - srcAlpha; + dest.r = color.r * srcAlpha + dest.r * invSrcAlpha; + dest.g = color.g * srcAlpha + dest.g * invSrcAlpha; + dest.b = color.b * srcAlpha + dest.b * invSrcAlpha; + dest.a = srcAlpha + dest.a * invSrcAlpha; } } } } + + // Convert RGBA buffer to BGR output rgbData.resize(rgbaBuffer.size() * 3); - for (int i = 0; i < rgbaBuffer.size(); ++i) { + for (size_t i = 0; i < rgbaBuffer.size(); ++i) { const Vec4& color = rgbaBuffer[i]; - int bgrIndex = i * 3; + size_t bgrIndex = i * 3; - // Convert from [0,1] to [0,255] and store as RGB - // rgbData.push_back(color.r); - // rgbData.push_back(color.g); - // rgbData.push_back(color.b); - rgbData[bgrIndex + 2] = static_cast(color.r * 255); - rgbData[bgrIndex + 1] = static_cast(color.g * 255); - rgbData[bgrIndex + 0] = static_cast(color.b * 255); - + // Convert from [0,1] to [0,255] and store as BGR + rgbData[bgrIndex + 2] = static_cast(color.r * 255); // R -> third position + rgbData[bgrIndex + 1] = static_cast(color.g * 255); // G -> second position + rgbData[bgrIndex + 0] = static_cast(color.b * 255); // B -> first position } } @@ -156,6 +240,68 @@ public: return spritesComped.size(); } +private: + // Helper function to extract pixel color from sprite data based on color format + Vec4 getSpritePixelColor(const std::vector& spriteData, + int x, int y, + size_t spriteWidth, size_t spriteHeight, + frame::colormap format) const { + size_t pixelIndex = y * spriteWidth + x; + size_t channels = 3; // Default to RGB + + switch (format) { + case frame::colormap::RGB: + channels = 3; + if (pixelIndex * channels + 2 < spriteData.size()) { + return Vec4(spriteData[pixelIndex * channels] / 255.0f, + spriteData[pixelIndex * channels + 1] / 255.0f, + spriteData[pixelIndex * channels + 2] / 255.0f, + 1.0f); + } + break; + + case frame::colormap::RGBA: + channels = 4; + if (pixelIndex * channels + 3 < spriteData.size()) { + return Vec4(spriteData[pixelIndex * channels] / 255.0f, + spriteData[pixelIndex * channels + 1] / 255.0f, + spriteData[pixelIndex * channels + 2] / 255.0f, + spriteData[pixelIndex * channels + 3] / 255.0f); + } + break; + + case frame::colormap::BGR: + channels = 3; + if (pixelIndex * channels + 2 < spriteData.size()) { + return Vec4(spriteData[pixelIndex * channels + 2] / 255.0f, // BGR -> RGB + spriteData[pixelIndex * channels + 1] / 255.0f, + spriteData[pixelIndex * channels] / 255.0f, + 1.0f); + } + break; + + case frame::colormap::BGRA: + channels = 4; + if (pixelIndex * channels + 3 < spriteData.size()) { + return Vec4(spriteData[pixelIndex * channels + 2] / 255.0f, // BGRA -> RGBA + spriteData[pixelIndex * channels + 1] / 255.0f, + spriteData[pixelIndex * channels] / 255.0f, + spriteData[pixelIndex * channels + 3] / 255.0f); + } + break; + + case frame::colormap::B: + channels = 1; + if (pixelIndex < spriteData.size()) { + float value = spriteData[pixelIndex] / 255.0f; + return Vec4(value, value, value, 1.0f); + } + break; + } + + // Return transparent black if out of bounds + return Vec4(0.0f, 0.0f, 0.0f, 0.0f); + } }; #endif \ No newline at end of file