|
|
@ -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; |
|
|
|
} |