| @ -0,0 +1,118 @@ | |||
| /* | |||
| cmake_minimum_required(VERSION 3.28) | |||
| project(codiceps) | |||
| set(CMAKE_CXX_STANDARD 23) | |||
| include(FetchContent) | |||
| FetchContent_Declare( | |||
| raylib | |||
| GIT_REPOSITORY https://github.com/raysan5/raylib.git | |||
| GIT_TAG 5.0 | |||
| ) | |||
| FetchContent_MakeAvailable(raylib) | |||
| add_executable(codiceps codiceps.cpp) | |||
| target_link_libraries(codiceps PUBLIC raylib_static) | |||
| */ | |||
| #include <ranges> | |||
| #include <iostream> | |||
| #include <fstream> | |||
| #include <sstream> | |||
| #include <vector> | |||
| #include <cassert> | |||
| #include "raylib.h" | |||
| std::string slurp(const char* filename) { | |||
| std::ifstream file{filename}; | |||
| std::stringstream data; | |||
| file >> data.rdbuf(); | |||
| return data.str(); | |||
| } | |||
| std::vector<bool> to_bits(std::string str) { | |||
| std::vector<bool> d; | |||
| for(char c : str) { | |||
| d.push_back(c & 0b10000000); | |||
| d.push_back(c & 0b1000000); | |||
| d.push_back(c & 0b100000); | |||
| d.push_back(c & 0b10000); | |||
| d.push_back(c & 0b1000); | |||
| d.push_back(c & 0b100); | |||
| d.push_back(c & 0b10); | |||
| d.push_back(c & 0b1); | |||
| } | |||
| return d; | |||
| } | |||
| std::string from_bits(std::vector<bool> data) { | |||
| std::string d; | |||
| for(auto composite : data | std::views::chunk(8)) { | |||
| char c = 0; | |||
| for(auto b : composite) { | |||
| c *= 2; | |||
| c += b; | |||
| } | |||
| d.push_back(c); | |||
| } | |||
| return d; | |||
| } | |||
| void encode(const char* pic, const char* data_source) { | |||
| auto image = LoadImage(pic); | |||
| auto raw_data = slurp(data_source); | |||
| std::vector<bool> raw_bits = to_bits(raw_data); | |||
| assert(raw_bits.size() % 8 == 0); | |||
| assert(raw_bits.size() / 8 == raw_data.size()); | |||
| size_t idx = 0; | |||
| for(auto j : std::ranges::iota_view(0, image.height)) { | |||
| for(auto i : std::ranges::iota_view(0, image.width)) { | |||
| auto c = GetImageColor(image, i, j); | |||
| c.r ^= c.r & 1; | |||
| c.g ^= c.g & 1; | |||
| c.b ^= c.b & 1; | |||
| c.r += idx < raw_bits.size() ? raw_bits[idx] : 0; idx++; | |||
| c.g += idx < raw_bits.size() ? raw_bits[idx] : 0; idx++; | |||
| c.b += idx < raw_bits.size() ? raw_bits[idx] : 0; idx++; | |||
| ImageDrawPixel(&image, i, j, c); | |||
| } | |||
| } | |||
| ExportImage(image, pic); | |||
| UnloadImage(image); | |||
| if(raw_bits.size() > idx) { | |||
| std::cout << "\x1b[1;31mWritten bits: " << raw_bits.size() << "/" << idx << std::endl; | |||
| } else { | |||
| std::cout << "\x1b[1;32mWritten bits: " << raw_bits.size() << "/" << idx << std::endl; | |||
| } | |||
| } | |||
| void decode(const char* pic) { | |||
| auto image = LoadImage(pic); | |||
| std::vector<bool> raw_bits; | |||
| size_t idx = 0; | |||
| for(auto j : std::ranges::iota_view(0, image.height)) { | |||
| for(auto i : std::ranges::iota_view(0, image.width)) { | |||
| auto c = GetImageColor(image, i, j); | |||
| raw_bits.push_back(c.r & 1); | |||
| raw_bits.push_back(c.g & 1); | |||
| raw_bits.push_back(c.b & 1); | |||
| } | |||
| } | |||
| UnloadImage(image); | |||
| std::cout << from_bits(raw_bits).c_str() << std::endl; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if(argc < 1) { | |||
| perror("arguments missing, expected: picture then datafile for coding, picture for decoding"); | |||
| } | |||
| if(argc == 3) { | |||
| encode(argv[1], argv[2]); | |||
| } else if(argc == 2) { | |||
| decode(argv[1]); | |||
| } | |||
| return 0; | |||
| } | |||