/*
|
|
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;
|
|
}
|