Some encode/decode practice.
This commit is contained in:
parent
8a41337e2d
commit
ff962a6b16
29 changed files with 727 additions and 305 deletions
|
@ -2,6 +2,8 @@
|
||||||
list(APPEND compression_LIB_INCLUDES
|
list(APPEND compression_LIB_INCLUDES
|
||||||
StreamCompressor.cpp
|
StreamCompressor.cpp
|
||||||
HuffmanEncoder.cpp
|
HuffmanEncoder.cpp
|
||||||
|
RunLengthEncoder.cpp
|
||||||
|
ZlibData.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(compression SHARED ${compression_LIB_INCLUDES})
|
add_library(compression SHARED ${compression_LIB_INCLUDES})
|
||||||
|
|
|
@ -1,12 +1,45 @@
|
||||||
#include "HuffmanEncoder.h"
|
#include "HuffmanEncoder.h"
|
||||||
|
|
||||||
#include "Tree.h"
|
#include "RawTree.h"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
void HuffmanEncoder::dumpNode(RawNode<CountPair>* node, unsigned depth) const
|
||||||
|
{
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = node->getData();
|
||||||
|
|
||||||
|
std::string prefix(depth, '_');
|
||||||
|
|
||||||
|
if (node->isLeaf())
|
||||||
|
{
|
||||||
|
std::cout << prefix << "Leaf with value: " << data.first << " and sum " << data.second << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << prefix << "Intermediate with sum " << data.second << std::endl;
|
||||||
|
std::cout << prefix << "Doing Left.." << std::endl;
|
||||||
|
dumpNode(node->getLeftChild(), depth+1);
|
||||||
|
|
||||||
|
std::cout << prefix << "Doing Right.." << std::endl;
|
||||||
|
dumpNode(node->getRightChild(), depth+1);
|
||||||
|
|
||||||
|
std::cout << prefix << "*****" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HuffmanEncoder::dumpTree(const RawTree<CountPair>& tree) const
|
||||||
|
{
|
||||||
|
dumpNode(tree.getRootNode(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
void HuffmanEncoder::encode(const HuffmanEncoder::DataStream& stream)
|
void HuffmanEncoder::encode(const HuffmanEncoder::DataStream& stream)
|
||||||
{
|
{
|
||||||
std::unordered_map<unsigned char, unsigned> counts;
|
std::unordered_map<unsigned char, unsigned> counts;
|
||||||
|
@ -14,64 +47,54 @@ void HuffmanEncoder::encode(const HuffmanEncoder::DataStream& stream)
|
||||||
{
|
{
|
||||||
counts[c]++;
|
counts[c]++;
|
||||||
}
|
}
|
||||||
|
encode(counts);
|
||||||
|
}
|
||||||
|
|
||||||
using CountPair = std::pair<unsigned char, unsigned>;
|
void HuffmanEncoder::encode(const std::unordered_map<unsigned char, unsigned>& counts)
|
||||||
auto cmp = [](CountPair left, CountPair right)
|
{
|
||||||
|
std::cout << "Counts" << std::endl;
|
||||||
|
for (const auto& data: counts)
|
||||||
{
|
{
|
||||||
return left.second > right.second;
|
std::cout << data.first << " | " << data.second << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << "*******" << std::endl;
|
||||||
|
|
||||||
|
auto cmp = [](RawNode<CountPair>* left, RawNode<CountPair>* right)
|
||||||
|
{
|
||||||
|
return left->getData().second > right->getData().second;
|
||||||
};
|
};
|
||||||
std::priority_queue<CountPair, std::vector<CountPair>, decltype(cmp)> q(cmp);
|
|
||||||
|
std::priority_queue<RawNode<CountPair>*, std::vector<RawNode<CountPair>* >, decltype(cmp)> q(cmp);
|
||||||
for (const auto& entry : counts)
|
for (const auto& entry : counts)
|
||||||
{
|
{
|
||||||
q.push({entry.first, entry.second});
|
q.push(new RawNode<CountPair>(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePtr<CountPair> lastNode;
|
while(q.size() > 1)
|
||||||
while(!q.empty())
|
|
||||||
{
|
{
|
||||||
const auto charData = q.top();
|
auto node0 = q.top();
|
||||||
auto characterNode = std::make_unique<Node<CountPair> >(charData);
|
|
||||||
q.pop();
|
q.pop();
|
||||||
|
|
||||||
if (!lastNode)
|
auto node1 = q.top();
|
||||||
{
|
|
||||||
const auto rightCharData = q.top();
|
|
||||||
auto rightCharacterNode = std::make_unique<Node<CountPair> >(rightCharData);
|
|
||||||
q.pop();
|
q.pop();
|
||||||
|
|
||||||
const auto sum = charData.second + rightCharData.second;
|
const auto sum = node0->getData().second + node1->getData().second;
|
||||||
CountPair data{0, sum};
|
auto new_node = new RawNode<CountPair>(CountPair{0, sum});
|
||||||
auto midNode = std::make_unique<Node<CountPair> >(data);
|
|
||||||
|
|
||||||
midNode->addChild(std::move(characterNode));
|
new_node->addChild(node0);
|
||||||
midNode->addChild(std::move(rightCharacterNode));
|
new_node->addChild(node1);
|
||||||
lastNode = std::move(midNode);
|
q.push(new_node);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto sum = lastNode->getData().second;
|
|
||||||
CountPair data{0, sum};
|
|
||||||
auto midNode = std::make_unique<Node<CountPair> >(data);
|
|
||||||
|
|
||||||
if (charData.second < lastNode->getData().second)
|
|
||||||
{
|
|
||||||
midNode->addChild(std::move(lastNode));
|
|
||||||
midNode->addChild(std::move(characterNode));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
midNode->addChild(std::move(characterNode));
|
|
||||||
midNode->addChild(std::move(lastNode));
|
|
||||||
}
|
|
||||||
lastNode = std::move(midNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tree<CountPair> tree;
|
auto root = q.top();
|
||||||
tree.addRootNode(std::move(lastNode));
|
q.pop();
|
||||||
|
|
||||||
|
RawTree<CountPair> tree;
|
||||||
|
tree.addRootNode(root);
|
||||||
|
|
||||||
//using TableEntry = std::tuple<>
|
//using TableEntry = std::tuple<>
|
||||||
|
|
||||||
|
dumpTree(tree);
|
||||||
|
|
||||||
std::cout << "********" << std::endl;
|
std::cout << "********" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "RawTree.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
class HuffmanEncoder
|
class HuffmanEncoder
|
||||||
{
|
{
|
||||||
using DataStream = std::vector<unsigned char>;
|
using DataStream = std::vector<unsigned char>;
|
||||||
|
using CountPair = std::pair<unsigned char, unsigned>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void encode(const DataStream& stream);
|
void encode(const DataStream& stream);
|
||||||
|
|
||||||
|
void encode(const std::unordered_map<unsigned char, unsigned>& counts);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void dumpTree(const RawTree<CountPair>& tree) const;
|
||||||
|
void dumpNode(RawNode<CountPair>* node, unsigned depth) const;
|
||||||
};
|
};
|
||||||
|
|
110
src/compression/RunLengthEncoder.h
Normal file
110
src/compression/RunLengthEncoder.h
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class RunLengthEncoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string encode(const std::string& string)
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
if (string.empty())
|
||||||
|
{
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char working_char{0};
|
||||||
|
unsigned count = 1;
|
||||||
|
for(unsigned idx=0; idx<string.size(); idx++)
|
||||||
|
{
|
||||||
|
auto c = string[idx];
|
||||||
|
if (idx == 0)
|
||||||
|
{
|
||||||
|
working_char = c;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == working_char)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
insertCharacter(ret, working_char, count);
|
||||||
|
working_char = c;
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insertCharacter(ret, working_char, count);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string decode(const std::string& string)
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
if (string.empty())
|
||||||
|
{
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned count{0};
|
||||||
|
while(count < string.size())
|
||||||
|
{
|
||||||
|
auto c = string[count];
|
||||||
|
if (c == mDelimiter)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
std::string reps;
|
||||||
|
char working_char{0};
|
||||||
|
while(count < string.size())
|
||||||
|
{
|
||||||
|
auto rep_char = string[count];
|
||||||
|
count++;
|
||||||
|
if (StringUtils::IsAlphabetical(rep_char))
|
||||||
|
{
|
||||||
|
working_char = rep_char;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reps += rep_char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned idx=0; idx<std::stoul(reps); idx++)
|
||||||
|
{
|
||||||
|
ret += working_char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret += c;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void insertCharacter(std::string& output, char c, unsigned count)
|
||||||
|
{
|
||||||
|
if (count >= 3)
|
||||||
|
{
|
||||||
|
output += mDelimiter + std::to_string(count) + c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (unsigned jdx=0;jdx<count; jdx++)
|
||||||
|
{
|
||||||
|
output += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char mDelimiter {'@'};
|
||||||
|
|
||||||
|
};
|
97
src/compression/ZlibData.h
Normal file
97
src/compression/ZlibData.h
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ByteUtils.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class ZlibData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void setByte(unsigned idx, unsigned char data)
|
||||||
|
{
|
||||||
|
mData[idx] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDataSize(std::size_t size)
|
||||||
|
{
|
||||||
|
mData = std::vector<unsigned char>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCompressionMethod(unsigned char method)
|
||||||
|
{
|
||||||
|
std::cout << "Got compression input " << static_cast<int>(method) << std::endl;
|
||||||
|
mCmf = method;
|
||||||
|
mCompressionMethod = ByteUtils::getLowerNBits(method, 4);
|
||||||
|
mCompressionInfo = ByteUtils::getHigherNBits(method, 4);
|
||||||
|
|
||||||
|
std::cout << "Got compression method " << static_cast<int>(mCompressionMethod) << " and info " << static_cast<int>(mCompressionInfo) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setExtraFlags(unsigned char extraFlags)
|
||||||
|
{
|
||||||
|
std::cout << "Got flags " << static_cast<int>(extraFlags) << std::endl;
|
||||||
|
|
||||||
|
mFlg = extraFlags;
|
||||||
|
mFlagCheck = ByteUtils::getLowerNBits(extraFlags, 5);
|
||||||
|
mFlagDict = ByteUtils::getBitN(extraFlags, 5);
|
||||||
|
mFlagLevel = ByteUtils::getHigherNBits(extraFlags, 2);
|
||||||
|
|
||||||
|
std::cout << "Got flag check " << static_cast<int>(mFlagCheck) << " and dict " << static_cast<int>(mFlagDict) << " and level " << static_cast<int>(mFlagLevel) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processData()
|
||||||
|
{
|
||||||
|
unsigned char NO_COMPRESSION = 0x00;
|
||||||
|
unsigned char FIXED_HUFFMAN = 0x01;
|
||||||
|
unsigned char DYNAMIC_HUFFMAN = 0x02;
|
||||||
|
unsigned char ERROR = 0x03;
|
||||||
|
|
||||||
|
bool in_final_block = false;
|
||||||
|
unsigned working_byte_id = 0;
|
||||||
|
for (unsigned idx=0; idx<mData.size(); idx++)
|
||||||
|
{
|
||||||
|
auto working_byte = mData[working_byte_id];
|
||||||
|
std::cout << "Into process data, byte is: " << static_cast<int>(working_byte) << std::endl;
|
||||||
|
|
||||||
|
auto final_block = ByteUtils::getBitN(working_byte, 0);
|
||||||
|
if (final_block)
|
||||||
|
{
|
||||||
|
std::cout << "Got final block" << std::endl;
|
||||||
|
in_final_block = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto compress_type = ByteUtils::getTwoBitsAtN(working_byte, 1);
|
||||||
|
std::cout << "Compress type byte is: " << static_cast<int>(compress_type) << std::endl;
|
||||||
|
|
||||||
|
if (compress_type == NO_COMPRESSION)
|
||||||
|
{
|
||||||
|
std::cout << "Got NO_COMPRESSION" << std::endl;
|
||||||
|
}
|
||||||
|
else if (compress_type == FIXED_HUFFMAN)
|
||||||
|
{
|
||||||
|
std::cout << "Got FIXED_HUFFMAN" << std::endl;
|
||||||
|
}
|
||||||
|
else if (compress_type == DYNAMIC_HUFFMAN)
|
||||||
|
{
|
||||||
|
std::cout << "Got DYNAMIC_HUFFMAN" << std::endl;
|
||||||
|
}
|
||||||
|
else if (compress_type == ERROR)
|
||||||
|
{
|
||||||
|
std::cout << "Got ERROR" << std::endl;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<unsigned char> mData;
|
||||||
|
unsigned char mCmf{0};
|
||||||
|
unsigned char mFlg{0};
|
||||||
|
unsigned char mCompressionMethod{0};
|
||||||
|
unsigned char mCompressionInfo{0};
|
||||||
|
unsigned char mFlagCheck{0};
|
||||||
|
unsigned char mFlagDict{0};
|
||||||
|
unsigned char mFlagLevel{0};
|
||||||
|
unsigned char mCheckValue{0};
|
||||||
|
};
|
|
@ -25,6 +25,46 @@ public:
|
||||||
return word & ByteUtils::WORD_LAST_BYTE;
|
return word & ByteUtils::WORD_LAST_BYTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned char getHigherNBits(unsigned char input, unsigned num)
|
||||||
|
{
|
||||||
|
return input >> 8 - num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char getLowerNBits(unsigned char input, unsigned num)
|
||||||
|
{
|
||||||
|
switch (num)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return input & 0x01;
|
||||||
|
case 2:
|
||||||
|
return input & 0x03;
|
||||||
|
case 3:
|
||||||
|
return input & 0x07;
|
||||||
|
case 4:
|
||||||
|
return input & 0x0F;
|
||||||
|
case 5:
|
||||||
|
return input & 0x1F;
|
||||||
|
case 6:
|
||||||
|
return input & 0x3F;
|
||||||
|
case 7:
|
||||||
|
return input & 0x7F;
|
||||||
|
case 8:
|
||||||
|
return input;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char getTwoBitsAtN(unsigned char input, unsigned n)
|
||||||
|
{
|
||||||
|
return (input & (0x03 << n)) >> n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char getBitN(unsigned char input, unsigned n)
|
||||||
|
{
|
||||||
|
return input & (1 << n);
|
||||||
|
}
|
||||||
|
|
||||||
static void ReverseBuffer(char* buffer, char* reverse, unsigned size, unsigned targetSize)
|
static void ReverseBuffer(char* buffer, char* reverse, unsigned size, unsigned targetSize)
|
||||||
{
|
{
|
||||||
for(unsigned idx=0; idx<targetSize; idx++)
|
for(unsigned idx=0; idx<targetSize; idx++)
|
||||||
|
|
|
@ -18,6 +18,7 @@ list(APPEND core_LIB_INCLUDES
|
||||||
Dictionary.cpp
|
Dictionary.cpp
|
||||||
Color.cpp
|
Color.cpp
|
||||||
CommandLineArgs.cpp
|
CommandLineArgs.cpp
|
||||||
|
data_structures/RawTree.cpp
|
||||||
data_structures/Tree.cpp
|
data_structures/Tree.cpp
|
||||||
loggers/FileLogger.cpp
|
loggers/FileLogger.cpp
|
||||||
file_utilities/Directory.cpp
|
file_utilities/Directory.cpp
|
||||||
|
|
|
@ -20,6 +20,11 @@ bool StringUtils::IsSpace(char c)
|
||||||
return std::isspace(c, loc);
|
return std::isspace(c, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StringUtils::IsAlphabetical(char c)
|
||||||
|
{
|
||||||
|
return std::isalpha(c);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> StringUtils::toLines(const std::string& input)
|
std::vector<std::string> StringUtils::toLines(const std::string& input)
|
||||||
{
|
{
|
||||||
auto result = std::vector<std::string>{};
|
auto result = std::vector<std::string>{};
|
||||||
|
|
|
@ -17,6 +17,9 @@ public:
|
||||||
static constexpr char COLON = ':';
|
static constexpr char COLON = ':';
|
||||||
|
|
||||||
static bool IsAlphaNumeric(char c);
|
static bool IsAlphaNumeric(char c);
|
||||||
|
|
||||||
|
static bool IsAlphabetical(char c);
|
||||||
|
|
||||||
static bool IsSpace(char c);
|
static bool IsSpace(char c);
|
||||||
static std::string ToLower(const std::string& s);
|
static std::string ToLower(const std::string& s);
|
||||||
static std::string convert(const std::wstring& input);
|
static std::string convert(const std::wstring& input);
|
||||||
|
|
0
src/core/data_structures/RawTree.cpp
Normal file
0
src/core/data_structures/RawTree.cpp
Normal file
97
src/core/data_structures/RawTree.h
Normal file
97
src/core/data_structures/RawTree.h
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class RawNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RawNode(T data)
|
||||||
|
: mData(data)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~RawNode()
|
||||||
|
{
|
||||||
|
if(mLeftChild)
|
||||||
|
{
|
||||||
|
delete mLeftChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mRightChild)
|
||||||
|
{
|
||||||
|
delete mRightChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addChild(RawNode* child)
|
||||||
|
{
|
||||||
|
if(!mLeftChild)
|
||||||
|
{
|
||||||
|
mLeftChild = child;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mRightChild = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLeaf() const
|
||||||
|
{
|
||||||
|
return !mLeftChild && !mRightChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
T getData() const
|
||||||
|
{
|
||||||
|
return mData;
|
||||||
|
}
|
||||||
|
|
||||||
|
RawNode* getLeftChild() const
|
||||||
|
{
|
||||||
|
return mLeftChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
RawNode* getRightChild() const
|
||||||
|
{
|
||||||
|
return mRightChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T mData;
|
||||||
|
unsigned char mTag{0};
|
||||||
|
RawNode* mLeftChild{nullptr};
|
||||||
|
RawNode* mRightChild{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class RawTree
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RawTree()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~RawTree()
|
||||||
|
{
|
||||||
|
if (mRootNode)
|
||||||
|
{
|
||||||
|
delete mRootNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addRootNode(RawNode<T>* root)
|
||||||
|
{
|
||||||
|
mRootNode = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
RawNode<T>* getRootNode() const
|
||||||
|
{
|
||||||
|
return mRootNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RawNode<T>* mRootNode{nullptr};
|
||||||
|
};
|
|
@ -32,6 +32,16 @@ public:
|
||||||
return mData;
|
return mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node* getLeftChild() const
|
||||||
|
{
|
||||||
|
return mLeftChild.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* getRightChild() const
|
||||||
|
{
|
||||||
|
return mRightChild.get();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T mData;
|
T mData;
|
||||||
unsigned char mTag{0};
|
unsigned char mTag{0};
|
||||||
|
|
|
@ -32,6 +32,11 @@ std::ofstream* File::GetOutHandle() const
|
||||||
return mOutHandle.get();
|
return mOutHandle.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char File::readNextByte()
|
||||||
|
{
|
||||||
|
return mInHandle->get();
|
||||||
|
}
|
||||||
|
|
||||||
void File::Open(bool asBinary)
|
void File::Open(bool asBinary)
|
||||||
{
|
{
|
||||||
if(mAccessMode == AccessMode::Read)
|
if(mAccessMode == AccessMode::Read)
|
||||||
|
|
|
@ -48,6 +48,8 @@ public:
|
||||||
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
|
unsigned char readNextByte();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,22 @@
|
||||||
list(APPEND image_HEADERS
|
list(APPEND image_HEADERS
|
||||||
Image.h
|
Image.h
|
||||||
PngWriter.h
|
PngWriter.h
|
||||||
PngWriterBasic.h
|
|
||||||
PngWriterImpl.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND image_LIB_INCLUDES
|
list(APPEND image_LIB_INCLUDES
|
||||||
Image.cpp
|
Image.cpp
|
||||||
PngWriter.cpp
|
PngWriter.cpp
|
||||||
PngWriterBasic.cpp
|
|
||||||
PngReader.cpp
|
PngReader.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND image_LIBS core)
|
list(APPEND image_LIBS core compression)
|
||||||
list(APPEND image_DEFINES "")
|
list(APPEND image_DEFINES "")
|
||||||
|
|
||||||
find_package(PNG QUIET)
|
|
||||||
if(PNG_FOUND)
|
|
||||||
list(APPEND image_LIBS PNG::PNG)
|
|
||||||
list(APPEND image_LIB_INCLUDES
|
|
||||||
PngWriterLibPng.cpp
|
|
||||||
)
|
|
||||||
list(APPEND image_DEFINES HAS_LIBPNG)
|
|
||||||
else()
|
|
||||||
message(STATUS "LIBRARY CHECK: libPNG not found - disabling libPNG based image i/o.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
add_library(image SHARED ${image_LIB_INCLUDES} ${image_HEADERS})
|
add_library(image SHARED ${image_LIB_INCLUDES} ${image_HEADERS})
|
||||||
#target_compile_definitions(image PRIVATE ${image_DEFINES})
|
#target_compile_definitions(image PRIVATE ${image_DEFINES})
|
||||||
|
|
||||||
target_include_directories(image PUBLIC
|
target_include_directories(image PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
)
|
|
||||||
set_target_properties( image PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
set_target_properties( image PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
||||||
|
|
||||||
target_link_libraries( image PUBLIC ${image_LIBS})
|
target_link_libraries( image PUBLIC ${image_LIBS})
|
||||||
|
|
|
@ -23,4 +23,19 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void drawAlternatingStrips(std::vector<unsigned char>& data, unsigned width, unsigned height, unsigned channels, unsigned bytesPerRow)
|
||||||
|
{
|
||||||
|
for(unsigned jdx=0;jdx<height;jdx++)
|
||||||
|
{
|
||||||
|
const auto heightOffset = jdx*bytesPerRow;
|
||||||
|
for(unsigned idx=0;idx<width*channels;idx+=channels)
|
||||||
|
{
|
||||||
|
const auto index = heightOffset + idx;
|
||||||
|
data[index] = (idx%2 == 0) ? 255*jdx/(height+1) : 0;
|
||||||
|
data[index+1] = 0;
|
||||||
|
data[index+2] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
33
src/image/PngElements.h
Normal file
33
src/image/PngElements.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Png
|
||||||
|
{
|
||||||
|
struct IHDRChunk
|
||||||
|
{
|
||||||
|
unsigned width{0};
|
||||||
|
unsigned height{0};
|
||||||
|
char bitDepth{0};
|
||||||
|
char colorType{0};
|
||||||
|
char compressionMethod{0};
|
||||||
|
char filterMethod{0};
|
||||||
|
char interlaceMethod{0};
|
||||||
|
|
||||||
|
std::string toString() const
|
||||||
|
{
|
||||||
|
std::stringstream sstr;
|
||||||
|
sstr << "width: " << width << "\n";
|
||||||
|
sstr << "height: " << height << "\n";
|
||||||
|
sstr << "bitDepth: " << (int)bitDepth << "\n";
|
||||||
|
sstr << "colorType: " << (int)colorType << "\n";
|
||||||
|
sstr << "compressionMethod: " << (int)compressionMethod << "\n";
|
||||||
|
sstr << "filterMethod: " << (int)filterMethod << "\n";
|
||||||
|
sstr << "interlaceMethod: " << (int)interlaceMethod << "\n";
|
||||||
|
return sstr.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include "PngReader.h"
|
#include "PngReader.h"
|
||||||
#include "BinaryStream.h"
|
|
||||||
|
|
||||||
#include "Image.h"
|
#include "BinaryStream.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@ -11,7 +10,7 @@ PngReader::~PngReader()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PngReader::setPath(const std::string& path)
|
void PngReader::setPath(const Path& path)
|
||||||
{
|
{
|
||||||
mPath = path;
|
mPath = path;
|
||||||
}
|
}
|
||||||
|
@ -57,12 +56,16 @@ bool PngReader::readChunk()
|
||||||
bool lastChunk = false;
|
bool lastChunk = false;
|
||||||
if (chunkType == "IHDR")
|
if (chunkType == "IHDR")
|
||||||
{
|
{
|
||||||
parseHeader();
|
readHeaderChunk();
|
||||||
}
|
}
|
||||||
else if(chunkType == "IEND")
|
else if(chunkType == "IEND")
|
||||||
{
|
{
|
||||||
lastChunk = true;
|
lastChunk = true;
|
||||||
}
|
}
|
||||||
|
else if(chunkType == "IDAT")
|
||||||
|
{
|
||||||
|
readIDATChunk(length);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for(unsigned idx=0;idx<length;idx++)
|
for(unsigned idx=0;idx<length;idx++)
|
||||||
|
@ -77,7 +80,29 @@ bool PngReader::readChunk()
|
||||||
return !lastChunk;
|
return !lastChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PngReader::parseHeader()
|
void PngReader::readIDATChunk(unsigned length)
|
||||||
|
{
|
||||||
|
if (mAwaitingDataBlock)
|
||||||
|
{
|
||||||
|
mImageData.setCompressionMethod(mFile->readNextByte());
|
||||||
|
mImageData.setExtraFlags(mFile->readNextByte());
|
||||||
|
mImageData.setDataSize(length-2);
|
||||||
|
for(unsigned idx=0; idx<length-2; idx++)
|
||||||
|
{
|
||||||
|
mImageData.setByte(idx, mFile->readNextByte());
|
||||||
|
}
|
||||||
|
mAwaitingDataBlock = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(unsigned idx=0; idx<length; idx++)
|
||||||
|
{
|
||||||
|
mImageData.setByte(idx, mFile->readNextByte());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PngReader::readHeaderChunk()
|
||||||
{
|
{
|
||||||
mIHDRChunk.width = *BinaryStream::getNextDWord(mFile->GetInHandle());
|
mIHDRChunk.width = *BinaryStream::getNextDWord(mFile->GetInHandle());
|
||||||
mIHDRChunk.height = *BinaryStream::getNextDWord(mFile->GetInHandle());
|
mIHDRChunk.height = *BinaryStream::getNextDWord(mFile->GetInHandle());
|
||||||
|
@ -94,18 +119,7 @@ void PngReader::parseHeader()
|
||||||
|
|
||||||
void PngReader::logHeader()
|
void PngReader::logHeader()
|
||||||
{
|
{
|
||||||
std::stringstream sstr;
|
std::cout << "IHDR\n" << mIHDRChunk.toString() << "*************\n";
|
||||||
sstr << "IHDR\n";
|
|
||||||
sstr << "width: " << mIHDRChunk.width << "\n";
|
|
||||||
sstr << "height: " << mIHDRChunk.height << "\n";
|
|
||||||
sstr << "bitDepth: " << (int)mIHDRChunk.bitDepth << "\n";
|
|
||||||
sstr << "colorType: " << (int)mIHDRChunk.colorType << "\n";
|
|
||||||
sstr << "compressionMethod: " << (int)mIHDRChunk.compressionMethod << "\n";
|
|
||||||
sstr << "filterMethod: " << (int)mIHDRChunk.filterMethod << "\n";
|
|
||||||
sstr << "interlaceMethod: " << (int)mIHDRChunk.interlaceMethod << "\n";
|
|
||||||
|
|
||||||
sstr << "************\n";
|
|
||||||
std::cout << sstr.str() << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Image<unsigned char> > PngReader::read()
|
std::unique_ptr<Image<unsigned char> > PngReader::read()
|
||||||
|
@ -125,6 +139,6 @@ std::unique_ptr<Image<unsigned char> > PngReader::read()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
mImageData.processData();
|
||||||
return std::move(image);
|
return std::move(image);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +1,42 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "File.h"
|
#include "File.h"
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
|
#include "PngElements.h"
|
||||||
|
#include "ZlibData.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
using Path = std::filesystem::path;
|
||||||
|
|
||||||
class PngReader
|
class PngReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~PngReader();
|
~PngReader();
|
||||||
void setPath(const std::string& path);
|
void setPath(const Path& path);
|
||||||
|
|
||||||
std::unique_ptr<Image<unsigned char> > read();
|
std::unique_ptr<Image<unsigned char> > read();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct IHDRChunk
|
|
||||||
{
|
|
||||||
unsigned width{0};
|
|
||||||
unsigned height{0};
|
|
||||||
char bitDepth{0};
|
|
||||||
char colorType{0};
|
|
||||||
char compressionMethod{0};
|
|
||||||
char filterMethod{0};
|
|
||||||
char interlaceMethod{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
bool readChunk();
|
bool readChunk();
|
||||||
void parseHeader();
|
|
||||||
|
void readHeaderChunk();
|
||||||
|
|
||||||
|
void readIDATChunk(unsigned length);
|
||||||
|
|
||||||
void logHeader();
|
void logHeader();
|
||||||
|
|
||||||
bool checkSignature();
|
bool checkSignature();
|
||||||
|
|
||||||
unsigned mCurrentOffset{0};
|
unsigned mCurrentOffset{0};
|
||||||
|
|
||||||
IHDRChunk mIHDRChunk;
|
Png::IHDRChunk mIHDRChunk;
|
||||||
|
|
||||||
std::unique_ptr<Image<unsigned char> > mWorkingImage;
|
std::unique_ptr<Image<unsigned char> > mWorkingImage;
|
||||||
std::unique_ptr<File> mFile;
|
std::unique_ptr<File> mFile;
|
||||||
std::string mPath;
|
Path mPath;
|
||||||
|
|
||||||
|
ZlibData mImageData;
|
||||||
|
bool mAwaitingDataBlock{true};
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,21 +2,11 @@
|
||||||
|
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
|
|
||||||
#ifdef HAS_LIBPNG
|
|
||||||
#include <png.h>
|
|
||||||
#include "PngWriterLibPng.h"
|
|
||||||
#else
|
|
||||||
#include "PngWriterBasic.h"
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
PngWriter::PngWriter()
|
PngWriter::PngWriter()
|
||||||
{
|
{
|
||||||
#ifdef HAS_LIBPNG
|
|
||||||
mImpl = std::make_unique<PngWriterLibPng>();
|
|
||||||
#else
|
|
||||||
mImpl = std::make_unique<PngWriterBasic>();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<PngWriter> PngWriter::Create()
|
std::unique_ptr<PngWriter> PngWriter::Create()
|
||||||
|
@ -24,14 +14,14 @@ std::unique_ptr<PngWriter> PngWriter::Create()
|
||||||
return std::make_unique<PngWriter>();
|
return std::make_unique<PngWriter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PngWriter::SetPath(const std::string& path)
|
void PngWriter::setPath(const Path& path)
|
||||||
{
|
{
|
||||||
mImpl->setPath(path);
|
mPath = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PngWriter::Write(const std::unique_ptr<Image<unsigned char> >& image) const
|
void PngWriter::write(const std::unique_ptr<Image<unsigned char> >& image) const
|
||||||
{
|
{
|
||||||
mImpl->write(image);
|
//mImpl->write(image);
|
||||||
//auto fp = fopen(mPath.c_str(), "wb");
|
//auto fp = fopen(mPath.c_str(), "wb");
|
||||||
|
|
||||||
//auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
//auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
|
|
||||||
class PngWriterImpl;
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
using Path = std::filesystem::path;
|
||||||
|
|
||||||
class PngWriter
|
class PngWriter
|
||||||
{
|
{
|
||||||
|
@ -14,13 +15,12 @@ public:
|
||||||
|
|
||||||
static std::unique_ptr<PngWriter> Create();
|
static std::unique_ptr<PngWriter> Create();
|
||||||
|
|
||||||
void SetPath(const std::string& path);
|
void setPath(const Path& path);
|
||||||
|
|
||||||
void Write(const std::unique_ptr<Image<unsigned char> >& image) const;
|
void write(const std::unique_ptr<Image<unsigned char> >& image) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<PngWriterImpl> mImpl;
|
Path mPath;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using PngWriterPtr = std::unique_ptr<PngWriter>;
|
using PngWriterPtr = std::unique_ptr<PngWriter>;
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
#include "PngWriterBasic.h"
|
|
||||||
|
|
||||||
void PngWriterBasic::setPath(const std::string& path)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void PngWriterBasic::write(const std::unique_ptr<Image<unsigned char> >& image) const
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "PngWriterImpl.h"
|
|
||||||
|
|
||||||
class PngWriterBasic : public PngWriterImpl
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void setPath(const std::string& path) override;
|
|
||||||
|
|
||||||
void write(const std::unique_ptr<Image<unsigned char>>& image) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string mPath;
|
|
||||||
};
|
|
|
@ -1,16 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "Image.h"
|
|
||||||
|
|
||||||
class PngWriterImpl
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~PngWriterImpl() = default;
|
|
||||||
|
|
||||||
virtual void setPath(const std::string& path) = 0;
|
|
||||||
|
|
||||||
virtual void write(const std::unique_ptr<Image<unsigned char> >& image) const = 0;
|
|
||||||
};
|
|
|
@ -1,14 +1,46 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "HuffmanEncoder.h"
|
#include "HuffmanEncoder.h"
|
||||||
|
#include "RunLengthEncoder.h"
|
||||||
|
|
||||||
|
void test_run_length_encoder()
|
||||||
|
{
|
||||||
|
std::string test_data = "BCAAAADDDCCACACAC";
|
||||||
|
|
||||||
|
RunLengthEncoder encoder;
|
||||||
|
auto encoded = encoder.encode(test_data);
|
||||||
|
|
||||||
|
std::cout << "Encoded: " << encoded << std::endl;
|
||||||
|
|
||||||
|
auto decoded = encoder.decode(encoded);
|
||||||
|
|
||||||
|
std::cout << "Decoded: " << decoded << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_huffman_encoder()
|
||||||
|
{
|
||||||
|
//std::string testData = "BCAADDDCCACACAC";
|
||||||
|
//std::vector<unsigned char> stream(testData.begin(), testData.end());
|
||||||
|
|
||||||
|
std::unordered_map<unsigned char, unsigned> counts;
|
||||||
|
counts['A'] = 1;
|
||||||
|
counts['B'] = 1;
|
||||||
|
counts['C'] = 1;
|
||||||
|
counts['D'] = 2;
|
||||||
|
counts['E'] = 3;
|
||||||
|
counts['F'] = 5;
|
||||||
|
counts['G'] = 5;
|
||||||
|
counts['H'] = 12;
|
||||||
|
|
||||||
|
HuffmanEncoder encoder;
|
||||||
|
encoder.encode(counts);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
std::string testData = "BCAADDDCCACACAC";
|
test_huffman_encoder();
|
||||||
std::vector<unsigned char> stream(testData.begin(), testData.end());
|
|
||||||
|
|
||||||
HuffmanEncoder encoder;
|
|
||||||
encoder.encode(stream);
|
|
||||||
|
|
||||||
|
//test_run_length_encoder();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
const auto path = "/home/jmsgrogan/code/MediaTool-build/bin/test.png";
|
const auto path = "/home/jmsgrogan/Downloads/test.png";
|
||||||
|
|
||||||
PngReader reader;
|
PngReader reader;
|
||||||
reader.setPath(path);
|
reader.setPath(path);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
#include "PngWriter.h"
|
#include "PngWriter.h"
|
||||||
#include "PngWriterImpl.h"
|
|
||||||
|
#include "ImagePrimitives.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
@ -13,22 +14,13 @@ int main()
|
||||||
image->setNumChannels(numChannels);
|
image->setNumChannels(numChannels);
|
||||||
|
|
||||||
std::vector<unsigned char> data(image->getBytesPerRow()*height, 0);
|
std::vector<unsigned char> data(image->getBytesPerRow()*height, 0);
|
||||||
for(unsigned jdx=0;jdx<height;jdx++)
|
|
||||||
{
|
ImagePrimitives::drawAlternatingStrips(data, width,height, numChannels, image->getBytesPerRow());
|
||||||
const auto heightOffset = jdx*image->getBytesPerRow();
|
|
||||||
for(unsigned idx=0;idx<width*numChannels;idx+=numChannels)
|
|
||||||
{
|
|
||||||
const auto index = heightOffset + idx;
|
|
||||||
data[index] = (idx%2 == 0) ? 255*jdx/(height+1) : 0;
|
|
||||||
data[index+1] = 0;
|
|
||||||
data[index+2] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
image->setData(data);
|
image->setData(data);
|
||||||
|
|
||||||
PngWriter writer;
|
PngWriter writer;
|
||||||
writer.SetPath("test.png");
|
writer.setPath("test.png");
|
||||||
writer.Write(image);
|
writer.write(image);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue