diff --git a/CMakeLists.txt b/CMakeLists.txt index a61cc78..526fcba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,12 @@ endif() set (PROJECTS #src/exe/cimbar - #src/lib/encoder + src/lib/bit_file + src/lib/cimb_translator + src/lib/encoder src/lib/extractor - #src/lib/util + src/lib/serialize + src/lib/util ) include_directories( diff --git a/src/lib/bit_file/CMakeLists.txt b/src/lib/bit_file/CMakeLists.txt new file mode 100644 index 0000000..1aef405 --- /dev/null +++ b/src/lib/bit_file/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) + +set(SOURCES + bitreader.h + bitwriter.h +) + +add_custom_target(bit_file SOURCES ${SOURCES}) + +add_subdirectory(test) diff --git a/src/lib/bit_file/bitreader.h b/src/lib/bit_file/bitreader.h new file mode 100644 index 0000000..6c29342 --- /dev/null +++ b/src/lib/bit_file/bitreader.h @@ -0,0 +1,60 @@ +#include +#include + +class bitreader +{ +public: + bitreader(const char* buffer, unsigned size) + : _buffer(buffer) + , _size(size) + , _position(0) + , _bitoffset(0) + { + } + + bool empty() const + { + return _position >= _size; + } + + unsigned readBit() + { + if (empty()) + return 0; + + unsigned bit = (_buffer[_position] & (1 << (7 - _bitoffset))); + ++_bitoffset; + if (_bitoffset == 8) + { + _position += 1; + _bitoffset -= 8; + } + return bit? 1 : 0; + } + + unsigned read(unsigned how_many_bits) + { + //auto [bytes, bits] = bytes_and_bits(how_many_bits, _bitoffset); + + unsigned res = 0; + for (unsigned i = 0; i < how_many_bits; ++i) + { + unsigned bit = readBit(); + res |= bit << (how_many_bits - i - 1); + } + return res; + } + + std::tuple bytes_and_bits(unsigned how_many_bits, unsigned bitoffset) const + { + unsigned remainder_bits = how_many_bits % 8; + unsigned bytes = how_many_bits / 8; + return {bytes, remainder_bits}; + } + +protected: + const char* _buffer; + unsigned _size; + unsigned _position; + unsigned _bitoffset; +}; diff --git a/src/lib/bit_file/bitwriter.h b/src/lib/bit_file/bitwriter.h new file mode 100644 index 0000000..409a346 --- /dev/null +++ b/src/lib/bit_file/bitwriter.h @@ -0,0 +1,20 @@ +#pragma once + +class bitwriter +{ +public: + bitwriter(char* buffer, unsigned size) + : _buffer(buffer) + , _size(size) + { + } + + bool writeBit(bool bit) + { + + } + +protected: + char* _buffer; + unsigned _size; +}; diff --git a/src/lib/bit_file/test/CMakeLists.txt b/src/lib/bit_file/test/CMakeLists.txt new file mode 100644 index 0000000..da50cc7 --- /dev/null +++ b/src/lib/bit_file/test/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 2.6) + +project(bit_file_test) + +set (SOURCES + test.cpp + bitreaderTest.cpp +) + +include_directories( + ${libcimbar_SOURCE_DIR}/test + ${libcimbar_SOURCE_DIR}/test/lib + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +add_executable ( + bit_file_test + ${SOURCES} +) + +add_test(bit_file_test bit_file_test) + diff --git a/src/lib/bit_file/test/bitreaderTest.cpp b/src/lib/bit_file/test/bitreaderTest.cpp new file mode 100644 index 0000000..7376d59 --- /dev/null +++ b/src/lib/bit_file/test/bitreaderTest.cpp @@ -0,0 +1,45 @@ +#include "unittest.h" + +#include "bitreader.h" +#include +#include +#include +#include + +TEST_CASE( "bitreaderTest/testSimple", "[unit]" ) +{ + std::string input = "Hello world"; + bitreader br(input.data(), input.size()); + assertEquals( 0x48, br.read(8) ); + assertEquals( 0x65, br.read(8) ); + assertEquals( 0x6c, br.read(8) ); + assertEquals( 0x6c, br.read(8) ); + assertEquals( 0x6f, br.read(8) ); + assertEquals( 0x20, br.read(8) ); +} + +TEST_CASE( "bitreaderTest/testDefault", "[unit]" ) +{ + std::string input = "Hello world"; + bitreader br(input.data(), input.size()); + + std::vector res; + while (!br.empty()) + res.push_back(br.read(8)); + + std::vector expected({0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}); + assertEquals(expected, res); +} + +TEST_CASE( "bitreaderTest/testPartial", "[unit]" ) +{ + std::string input = "Hello world"; + bitreader br(input.data(), input.size()); + + std::vector res; + while (!br.empty()) + res.push_back(br.read(5)); + + std::vector expected({9, 1, 18, 22, 24, 27, 3, 15, 4, 1, 27, 22, 30, 28, 19, 12, 12, 16}); + assertEquals(expected, res); +} diff --git a/src/lib/bit_file/test/test.cpp b/src/lib/bit_file/test/test.cpp new file mode 100644 index 0000000..178916e --- /dev/null +++ b/src/lib/bit_file/test/test.cpp @@ -0,0 +1,3 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + diff --git a/src/lib/cimb_translator/CMakeLists.txt b/src/lib/cimb_translator/CMakeLists.txt new file mode 100644 index 0000000..c663e16 --- /dev/null +++ b/src/lib/cimb_translator/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) + +set(SOURCES + CimbWriter.cpp + CimbWriter.h +) + +add_library(cimb_translator STATIC ${SOURCES}) + +add_subdirectory(test) diff --git a/src/lib/cimb_translator/CimbWriter.cpp b/src/lib/cimb_translator/CimbWriter.cpp new file mode 100644 index 0000000..56080e9 --- /dev/null +++ b/src/lib/cimb_translator/CimbWriter.cpp @@ -0,0 +1,43 @@ +#include "CimbWriter.h" + +#include "serialize/format.h" +#include +#include +using std::string; + +namespace { + string getTileDir(unsigned tile_bits) + { + return fmt::format("{}/bitmap/{}", LIBCIMBAR_PROJECT_ROOT, tile_bits); + } +} + +CimbWriter::CimbWriter(unsigned tile_bits) +{ + _numSymbols = (1 << tile_bits); + load_tiles(getTileDir(tile_bits)); +} + +cv::Mat CimbWriter::load_tile(string tile_dir, unsigned index) +{ + string imgPath = fmt::format("{}/{:02x}.png", tile_dir, index); + std::cout << imgPath << std::endl; + cv::Mat temp = cv::imread(imgPath); + return temp; +} + +// dir will need to be passed via env? Doesn't make sense to compile it in, and doesn't *really* make sense to use cwd +bool CimbWriter::load_tiles(std::string tile_dir) +{ + for (unsigned i = 0; i < _numSymbols; ++i) + _tiles.push_back(load_tile(tile_dir, i)); + return true; +} + +const cv::Mat& CimbWriter::encode(unsigned bits) const +{ + unsigned symbol = bits % _numSymbols; + unsigned color = bits / _numSymbols; + std::cout << fmt::format("symbol {}, color {}", symbol, color) << std::endl; + return _tiles[symbol]; +} diff --git a/src/lib/cimb_translator/CimbWriter.h b/src/lib/cimb_translator/CimbWriter.h new file mode 100644 index 0000000..c23ddda --- /dev/null +++ b/src/lib/cimb_translator/CimbWriter.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include + +class CimbWriter +{ +public: + CimbWriter(unsigned tile_bits=4); + + cv::Mat load_tile(std::string tile_dir, unsigned index); + bool load_tiles(std::string tile_dir); + + const cv::Mat& encode(unsigned bits) const; + +protected: + std::vector _tiles; + unsigned _numSymbols; +}; diff --git a/src/lib/cimb_translator/test/CMakeLists.txt b/src/lib/cimb_translator/test/CMakeLists.txt new file mode 100644 index 0000000..4997c66 --- /dev/null +++ b/src/lib/cimb_translator/test/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.10) + +project(cimb_translator_test) + +set (SOURCES + test.cpp + CimbWriterTest.cpp +) + +include_directories( + ${libcimbar_SOURCE_DIR}/test + ${libcimbar_SOURCE_DIR}/test/lib + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +add_executable ( + cimb_translator_test + ${SOURCES} +) + +add_test(cimb_translator_test cimb_translator_test) + +target_link_libraries(cimb_translator_test + cimb_translator + + opencv_core + opencv_imgcodecs + opencv_imgproc + opencv_photo +) + diff --git a/src/lib/cimb_translator/test/CimbWriterTest.cpp b/src/lib/cimb_translator/test/CimbWriterTest.cpp new file mode 100644 index 0000000..b9fc0e4 --- /dev/null +++ b/src/lib/cimb_translator/test/CimbWriterTest.cpp @@ -0,0 +1,18 @@ +#include "unittest.h" + +#include "CimbWriter.h" + +#include + +#include +#include +#include + +TEST_CASE( "CimbWriterTest/testSimple", "[unit]" ) +{ + CimbWriter cw(4); + cv::Mat res = cw.encode(15); + + std::cout << res << std::endl; +} + diff --git a/src/lib/cimb_translator/test/test.cpp b/src/lib/cimb_translator/test/test.cpp new file mode 100644 index 0000000..178916e --- /dev/null +++ b/src/lib/cimb_translator/test/test.cpp @@ -0,0 +1,3 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + diff --git a/src/lib/encoder/CMakeLists.txt b/src/lib/encoder/CMakeLists.txt new file mode 100644 index 0000000..f04cef4 --- /dev/null +++ b/src/lib/encoder/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.10) + +set(SOURCES + Decoder.cpp + Decoder.h + Encoder.cpp + Encoder.h +) + +add_library(encoder STATIC ${SOURCES}) + +add_subdirectory(test) diff --git a/src/lib/encoder/Decoder.cpp b/src/lib/encoder/Decoder.cpp new file mode 100644 index 0000000..d1b7fc2 --- /dev/null +++ b/src/lib/encoder/Decoder.cpp @@ -0,0 +1,39 @@ +#include "Decoder.h" + +#include "bit_file/bitreader.h" +#include "util/File.h" + +#include +using std::string; + +Decoder::Decoder() +{ +} + +/* while bits == f.read_tile() + * decode(bits) + * + * bitwriter bw("output.txt"); + * img = open("foo.png") + * for tileX, tileY in img + * bits = decoder.decode(img, tileX, tileY) + * bw.write(bits) + * if bw.shouldFlush() + * bw.flush() + * + * */ + + +unsigned Decoder::decode(string filename) +{ + unsigned bits_per_op = 6; + + File f(filename); + + char buffer[8192]; + while (f.good()) + { + bitreader br(buffer, 8192); + unsigned bits = br.read(bits_per_op); + } +} diff --git a/src/lib/encoder/Decoder.h b/src/lib/encoder/Decoder.h new file mode 100644 index 0000000..bc5a836 --- /dev/null +++ b/src/lib/encoder/Decoder.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class Decoder +{ +public: + Decoder(); // pass in handler interface + + unsigned decode(std::string filename); + +protected: +}; diff --git a/src/lib/encoder/Encoder.cpp b/src/lib/encoder/Encoder.cpp new file mode 100644 index 0000000..fb992d6 --- /dev/null +++ b/src/lib/encoder/Encoder.cpp @@ -0,0 +1,45 @@ +#include "Encoder.h" + +#include "bit_file/bitreader.h" +#include "util/File.h" + +#include +using std::string; + +Encoder::Encoder(unsigned bits_per_symbol, unsigned bits_per_color) + : _bitsPerSymbol(bits_per_symbol) + , _bitsPerColor(bits_per_color) +{ +} + +/* while bits == f.read(buffer, 8192) + * encode(bits) + * + * char buffer[2000]; + * while f.read(buffer) + * bit_buffer bb(buffer) + * while bb + * bits1 = bb.get(_bitsPerSymbol) + * bits2 = bb.get(_bitsPerColor) + * + * */ + + +unsigned Encoder::encode(string filename, string output) +{ + unsigned bits_per_op = _bitsPerColor + _bitsPerSymbol; + unsigned readSize = 8192; // should be a multiple of bits_per_op and 8 + + char buffer[readSize]; + File f(filename); + while (f.good()) + { + unsigned bytes = f.read(buffer, readSize); + bitreader br(buffer, bytes); + while (!br.empty()) + { + unsigned bits = br.read(bits_per_op); + std::cout << "read " << bits << std::endl; + } + } +} diff --git a/src/lib/encoder/Encoder.h b/src/lib/encoder/Encoder.h new file mode 100644 index 0000000..74cabc0 --- /dev/null +++ b/src/lib/encoder/Encoder.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +class Encoder +{ +public: + Encoder(unsigned bits_per_symbol, unsigned bits_per_color); // pass in handler interface + + unsigned encode(std::string filename, std::string output); + +protected: + unsigned _bitsPerSymbol; + unsigned _bitsPerColor; +}; diff --git a/src/lib/encoder/test/CMakeLists.txt b/src/lib/encoder/test/CMakeLists.txt new file mode 100644 index 0000000..16306c0 --- /dev/null +++ b/src/lib/encoder/test/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.10) + +project(encoder_test) + +set (SOURCES + test.cpp + EncoderTest.cpp +) + +include_directories( + ${libcimbar_SOURCE_DIR}/test + ${libcimbar_SOURCE_DIR}/test/lib + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +add_executable ( + encoder_test + ${SOURCES} +) + +add_test(encoder_test encoder_test) + +target_link_libraries(encoder_test + encoder +) + diff --git a/src/lib/encoder/test/EncoderTest.cpp b/src/lib/encoder/test/EncoderTest.cpp new file mode 100644 index 0000000..50df1f1 --- /dev/null +++ b/src/lib/encoder/test/EncoderTest.cpp @@ -0,0 +1,19 @@ +#include "unittest.h" + +#include "encoder/Encoder.h" +#include "util/File.h" + +#include +#include +#include +#include + +TEST_CASE( "EncoderTest/testDefault", "[unit]" ) +{ + std::string input = "Hello world"; + File f("/tmp/test.txt"); + f.write(input.data(), input.size()); + + Encoder enc(4, 2); + enc.encode("/tmp/test.txt", "/tmp/doesntmatteryet.txt"); +} diff --git a/src/lib/encoder/test/test.cpp b/src/lib/encoder/test/test.cpp new file mode 100644 index 0000000..178916e --- /dev/null +++ b/src/lib/encoder/test/test.cpp @@ -0,0 +1,3 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + diff --git a/src/lib/serialize/CMakeLists.txt b/src/lib/serialize/CMakeLists.txt new file mode 100644 index 0000000..940fc84 --- /dev/null +++ b/src/lib/serialize/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.10) + +set(SOURCES + format.h +) + +add_custom_target(serialize SOURCES ${SOURCES}) + diff --git a/src/lib/serialize/format.h b/src/lib/serialize/format.h new file mode 100644 index 0000000..261800e --- /dev/null +++ b/src/lib/serialize/format.h @@ -0,0 +1,5 @@ +#pragma once + +#define FMT_HEADER_ONLY +#include "cppformat/format.h" + diff --git a/src/lib/util/CMakeLists.txt b/src/lib/util/CMakeLists.txt new file mode 100644 index 0000000..6ebdc45 --- /dev/null +++ b/src/lib/util/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 2.6) + +set(SOURCES + File.h +) + +add_custom_target(util SOURCES ${SOURCES}) + diff --git a/src/lib/util/File.h b/src/lib/util/File.h new file mode 100644 index 0000000..0df08d6 --- /dev/null +++ b/src/lib/util/File.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +class File +{ +public: + File(std::string filename, bool write=false) + { + _fp = fopen(filename.c_str(), write? "wb" : "rb"); + _good = true; + } + + unsigned read(char* buffer, unsigned bytes) + { + unsigned res = fread(buffer, sizeof(char), bytes, _fp); + if (res != bytes) + _good = false; + return res; + } + + unsigned write(char* buffer, unsigned bytes) + { + unsigned res = fwrite(buffer, sizeof(char), bytes, _fp); + if (res != bytes) + _good = false; + return res; + } + + ~File() + { + if (_fp != NULL) + fclose(_fp); + } + + bool good() const + { + return _good; + } + +protected: + FILE* _fp; + bool _good; +};