From 7a8ad131ed90894ef5b097f30c6962cf4123ff6d Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Thu, 11 Nov 2021 22:15:51 +0900 Subject: [PATCH] WIP libpng image decoding Also added Doxyfile for generation of doxygen-based documentation. --- .gitignore | 1 + .lvimrc | 2 + CMakeLists.txt | 10 ++ Doxyfile | 332 ++++++++++++++++++++++++++++++++++++++++++++ doxygen/mainpage.md | 3 + src/image.cc | 131 +++++++++++++++++ src/image.h | 72 ++++++++++ 7 files changed, 551 insertions(+) create mode 100644 Doxyfile create mode 100644 doxygen/mainpage.md create mode 100644 src/image.cc create mode 100644 src/image.h 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