diff --git a/.gitignore b/.gitignore
index eb32573..7347907 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
build*/
compile_commands.json
.cache/
+doxygen_out/
diff --git a/.lvimrc b/.lvimrc
index b32befc..976bb70 100644
--- a/.lvimrc
+++ b/.lvimrc
@@ -1,2 +1,4 @@
+set expandtab
set tabstop=2
set shiftwidth=2
+set textwidth=80
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a0317a4..b850316 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,6 +3,7 @@ project(EN605.617.81.FA21_StephenSeo_DitheringProject)
set(Project_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/image.cc
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
@@ -16,3 +17,12 @@ endif()
add_executable(DitheringProject
${Project_SOURCES})
+
+find_package(PNG REQUIRED)
+
+target_include_directories(DitheringProject PUBLIC
+ ${PNG_INCLUDE_DIRS}
+)
+target_link_libraries(DitheringProject PUBLIC
+ ${PNG_LIBRARIES}
+)
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..b0a3cad
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,332 @@
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME = "EN605.617.81.FA21_StephenSeo_DitheringProject"
+PROJECT_NUMBER =
+PROJECT_BRIEF = "Dithers an image using OpenCL"
+PROJECT_LOGO =
+OUTPUT_DIRECTORY = "doxygen_out"
+CREATE_SUBDIRS = NO
+ALLOW_UNICODE_NAMES = NO
+OUTPUT_LANGUAGE = English
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH =
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+JAVADOC_BANNER = NO
+QT_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+PYTHON_DOCSTRING = YES
+INHERIT_DOCS = YES
+SEPARATE_MEMBER_PAGES = NO
+TAB_SIZE = 2
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+OPTIMIZE_FOR_FORTRAN = NO
+OPTIMIZE_OUTPUT_VHDL = NO
+OPTIMIZE_OUTPUT_SLICE = NO
+EXTENSION_MAPPING =
+MARKDOWN_SUPPORT = YES
+TOC_INCLUDE_HEADINGS = 5
+AUTOLINK_SUPPORT = YES
+BUILTIN_STL_SUPPORT = YES
+CPP_CLI_SUPPORT = NO
+SIP_SUPPORT = NO
+IDL_PROPERTY_SUPPORT = YES
+DISTRIBUTE_GROUP_DOC = NO
+GROUP_NESTED_COMPOUNDS = NO
+SUBGROUPING = YES
+INLINE_GROUPED_CLASSES = NO
+INLINE_SIMPLE_STRUCTS = NO
+TYPEDEF_HIDES_STRUCT = NO
+LOOKUP_CACHE_SIZE = 0
+NUM_PROC_THREADS = 1
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_PRIV_VIRTUAL = NO
+EXTRACT_PACKAGE = NO
+EXTRACT_STATIC = NO
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = NO
+EXTRACT_ANON_NSPACES = NO
+RESOLVE_UNNAMED_PARAMS = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+HIDE_COMPOUND_REFERENCE= NO
+SHOW_HEADERFILE = YES
+SHOW_INCLUDE_FILES = YES
+SHOW_GROUPED_MEMB_INC = NO
+FORCE_LOCAL_INCLUDES = NO
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_MEMBERS_CTORS_1ST = NO
+SORT_GROUP_NAMES = NO
+SORT_BY_SCOPE_NAME = NO
+STRICT_PROTO_MATCHING = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+SHOW_FILES = YES
+SHOW_NAMESPACES = YES
+FILE_VERSION_FILTER =
+LAYOUT_FILE =
+CITE_BIB_FILES =
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_IF_INCOMPLETE_DOC = YES
+WARN_NO_PARAMDOC = NO
+WARN_AS_ERROR = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+INPUT = doxygen src
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.l \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f18 \
+ *.f \
+ *.for \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf \
+ *.ice
+RECURSIVE = NO
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS = *
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+FILTER_SOURCE_PATTERNS =
+USE_MDFILE_AS_MAINPAGE = doxygen/mainpage.md
+SOURCE_BROWSER = NO
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION = NO
+REFERENCES_LINK_SOURCE = YES
+SOURCE_TOOLTIPS = YES
+USE_HTAGS = NO
+VERBATIM_HEADERS = YES
+ALPHABETICAL_INDEX = YES
+IGNORE_PREFIX =
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_EXTRA_STYLESHEET =
+HTML_EXTRA_FILES =
+HTML_COLORSTYLE_HUE = 220
+HTML_COLORSTYLE_SAT = 100
+HTML_COLORSTYLE_GAMMA = 80
+HTML_TIMESTAMP = NO
+HTML_DYNAMIC_MENUS = YES
+HTML_DYNAMIC_SECTIONS = NO
+HTML_INDEX_NUM_ENTRIES = 100
+GENERATE_DOCSET = NO
+DOCSET_FEEDNAME = "Doxygen generated docs"
+DOCSET_BUNDLE_ID = org.doxygen.Project
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+DOCSET_PUBLISHER_NAME = Publisher
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+CHM_INDEX_ENCODING =
+BINARY_TOC = NO
+TOC_EXPAND = NO
+GENERATE_QHP = NO
+QCH_FILE =
+QHP_NAMESPACE = org.doxygen.Project
+QHP_VIRTUAL_FOLDER = doc
+QHP_CUST_FILTER_NAME =
+QHP_CUST_FILTER_ATTRS =
+QHP_SECT_FILTER_ATTRS =
+QHG_LOCATION =
+GENERATE_ECLIPSEHELP = NO
+ECLIPSE_DOC_ID = org.doxygen.Project
+DISABLE_INDEX = NO
+GENERATE_TREEVIEW = NO
+FULL_SIDEBAR = NO
+ENUM_VALUES_PER_LINE = 4
+TREEVIEW_WIDTH = 250
+EXT_LINKS_IN_WINDOW = NO
+HTML_FORMULA_FORMAT = png
+FORMULA_FONTSIZE = 10
+FORMULA_TRANSPARENT = YES
+FORMULA_MACROFILE =
+USE_MATHJAX = NO
+MATHJAX_VERSION = MathJax_2
+MATHJAX_FORMAT = HTML-CSS
+MATHJAX_RELPATH =
+MATHJAX_EXTENSIONS =
+MATHJAX_CODEFILE =
+SEARCHENGINE = YES
+SERVER_BASED_SEARCH = NO
+EXTERNAL_SEARCH = NO
+SEARCHENGINE_URL =
+SEARCHDATA_FILE = searchdata.xml
+EXTERNAL_SEARCH_ID =
+EXTRA_SEARCH_MAPPINGS =
+GENERATE_LATEX = YES
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME =
+MAKEINDEX_CMD_NAME = makeindex
+LATEX_MAKEINDEX_CMD = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4
+EXTRA_PACKAGES =
+LATEX_HEADER =
+LATEX_FOOTER =
+LATEX_EXTRA_STYLESHEET =
+LATEX_EXTRA_FILES =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = YES
+LATEX_BATCHMODE = NO
+LATEX_HIDE_INDICES = NO
+LATEX_BIB_STYLE = plain
+LATEX_TIMESTAMP = NO
+LATEX_EMOJI_DIRECTORY =
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_SUBDIR =
+MAN_LINKS = NO
+GENERATE_XML = NO
+XML_OUTPUT = xml
+XML_PROGRAMLISTING = YES
+XML_NS_MEMB_FILE_SCOPE = NO
+GENERATE_DOCBOOK = NO
+DOCBOOK_OUTPUT = docbook
+GENERATE_AUTOGEN_DEF = NO
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+EXTERNAL_PAGES = YES
+CLASS_DIAGRAMS = YES
+DIA_PATH =
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = NO
+DOT_NUM_THREADS = 0
+DOT_FONTNAME = Helvetica
+DOT_FONTSIZE = 10
+DOT_FONTPATH =
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+UML_LOOK = NO
+UML_LIMIT_NUM_FIELDS = 10
+DOT_UML_DETAILS = NO
+DOT_WRAP_THRESHOLD = 17
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO
+CALLER_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
+DOT_IMAGE_FORMAT = png
+INTERACTIVE_SVG = NO
+DOT_PATH =
+DOTFILE_DIRS =
+MSCFILE_DIRS =
+DIAFILE_DIRS =
+PLANTUML_JAR_PATH =
+PLANTUML_CFG_FILE =
+PLANTUML_INCLUDE_PATH =
+DOT_GRAPH_MAX_NODES = 50
+MAX_DOT_GRAPH_DEPTH = 0
+DOT_TRANSPARENT = NO
+DOT_MULTI_TARGETS = NO
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
diff --git a/doxygen/mainpage.md b/doxygen/mainpage.md
new file mode 100644
index 0000000..87759b3
--- /dev/null
+++ b/doxygen/mainpage.md
@@ -0,0 +1,3 @@
+# Dithering Project
+
+Classes
diff --git a/src/image.cc b/src/image.cc
new file mode 100644
index 0000000..8f8d9b9
--- /dev/null
+++ b/src/image.cc
@@ -0,0 +1,131 @@
+#include "image.h"
+
+#include
+#include
+#include
+
+#include
+
+Image::Image() : data_(), width_(0), height_(0), is_grayscale_(true) {}
+
+Image::Image(const char *filename) : Image(std::string(filename)) {}
+
+Image::Image(const std::string &filename)
+ : data_(), width_(0), height_(0), is_grayscale_(true) {
+ if (filename.compare(filename.size() - 4, filename.size(), ".png") == 0) {
+ // filename expected to be .png
+ DecodePNG(filename);
+ } else if (filename.compare(filename.size() - 4, filename.size(), ".pgm") ==
+ 0) {
+ // filename expected to be .pgm
+ DecodePGM(filename);
+ } else if (filename.compare(filename.size() - 4, filename.size(), ".ppm") ==
+ 0) {
+ // filename expected to be .ppm
+ DecodePPM(filename);
+ } else {
+ // unknown filename extension
+ return;
+ }
+}
+
+bool Image::IsValid() const {
+ return !data_.empty() && width_ > 0 && height_ > 0;
+}
+
+uint8_t *Image::GetData() { return data_.data(); }
+
+const uint8_t *Image::GetData() const { return data_.data(); }
+
+unsigned int Image::GetSize() const { return data_.size(); }
+
+unsigned int Image::GetWidth() const { return width_; }
+
+unsigned int Image::GetHeight() const { return height_; }
+
+bool Image::IsGrayscale() const { return is_grayscale_; }
+
+void Image::DecodePNG(const std::string &filename) {
+ FILE *file = std::fopen(filename.c_str(), "rb");
+
+ // Check header of file to check if it is actually a png file.
+ {
+ std::array buf;
+ if (std::fread(buf.data(), 1, 8, file) != 8) {
+ std::cout << "ERROR: File \"" << filename << "\" is smaller than 8 bytes"
+ << std::endl;
+ std::fclose(file);
+ return;
+ } else if (!png_sig_cmp(reinterpret_cast(buf.data()), 0,
+ 8)) {
+ // not png file, do nothing
+ std::fclose(file);
+ return;
+ }
+ }
+
+ // seek to head of file
+ std::rewind(file);
+
+ // init required structs for png decoding
+ png_structp png_ptr =
+ png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (!png_ptr) {
+ std::cout << "ERROR: Failed to initialize libpng (png_ptr) for decoding "
+ "PNG file \""
+ << filename << '"' << std::endl;
+ std::fclose(file);
+ return;
+ }
+
+ png_infop png_info_ptr = png_create_info_struct(png_ptr);
+ if (!png_info_ptr) {
+ std::cout << "ERROR: Failed to initialize libpng (png_infop) for decoding "
+ "PNG file \""
+ << filename << '"' << std::endl;
+ png_destroy_read_struct(&png_ptr, nullptr, nullptr);
+ std::fclose(file);
+ return;
+ }
+
+ png_infop png_end_info_ptr = png_create_info_struct(png_ptr);
+ if (!png_end_info_ptr) {
+ std::cout << "ERROR: Failed to initialize libpng (end png_infop) for "
+ "decoding PNG file \""
+ << filename << '"' << std::endl;
+ png_destroy_read_struct(&png_ptr, &png_info_ptr, nullptr);
+ std::fclose(file);
+ return;
+ }
+
+ // required to handle libpng errors
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_read_struct(&png_ptr, &png_info_ptr, &png_end_info_ptr);
+ std::fclose(file);
+ return;
+ }
+
+ // pass the FILE pointer to libpng
+ png_init_io(png_ptr, file);
+
+ // TODO BEGIN
+ //// have libpng process the png data
+ // png_read_png(png_ptr, png_info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
+
+ //// get rows of pixels from libpng
+ // png_bytep row_pointer = nullptr;
+ // png_read_row(png_ptr, row_pointer, nullptr);
+
+ // TODO END
+
+ // cleanup
+ png_destroy_read_struct(&png_ptr, &png_info_ptr, &png_end_info_ptr);
+}
+
+void Image::DecodePGM(const std::string &filename) {
+ // TODO
+}
+
+void Image::DecodePPM(const std::string &filename) {
+ // TODO
+}
diff --git a/src/image.h b/src/image.h
new file mode 100644
index 0000000..db5730a
--- /dev/null
+++ b/src/image.h
@@ -0,0 +1,72 @@
+#ifndef IGPUP_DITHERING_PROJECT_IMAGE_H_
+#define IGPUP_DITHERING_PROJECT_IMAGE_H_
+
+#include
+#include
+#include
+
+class Image {
+ public:
+ Image();
+
+ /*!
+ * \brief Decodes the given file's data and stores the pixels internally.
+ *
+ * Use IsValid() to check if the file was successfully decoded.
+ *
+ * Image supports decoding .png, .pgm, and .ppm . Decoding only checks the
+ * filename suffix as a guide on which file-type to expect.
+ */
+ Image(const char *filename);
+
+ /// Same constructor as Image(const char *filename)
+ Image(const std::string &filename);
+
+ // allow copy
+ Image(const Image &other) = default;
+ Image &operator=(const Image &other) = default;
+
+ // allow move
+ Image(Image &&other) = default;
+ Image &operator=(Image &&other) = default;
+
+ /*!
+ * \brief Is true if the Image instance is valid.
+ *
+ * This will return false on an Image instance that failed to decode an input
+ * image when constructed.
+ */
+ bool IsValid() const;
+
+ /// Returns a raw pointer to the image's data.
+ uint8_t *GetData();
+ /// Returns a const raw pointer to the image's data.
+ const uint8_t *GetData() const;
+
+ /*!
+ * \brief Returns the number of bytes in the image.
+ *
+ * For grayscale images, each pixel is one byte.
+ * For colored images, each pixel is 4 bytes: R, G, B, and Alpha.
+ */
+ unsigned int GetSize() const;
+ /// Returns the width of the image.
+ unsigned int GetWidth() const;
+ /// Returns the height of the image.
+ unsigned int GetHeight() const;
+
+ /// Returns true if the image is grayscale. If false, then the image is RGBA.
+ bool IsGrayscale() const;
+
+ private:
+ std::vector data_;
+ unsigned int width_;
+ unsigned int height_;
+ bool is_grayscale_;
+
+ void DecodePNG(const std::string &filename);
+ void DecodePGM(const std::string &filename);
+ void DecodePPM(const std::string &filename);
+};
+
+#endif