commit 20f656266033b32e80b748df38d719c11a0627ea Author: Redacted Date: Tue Jun 11 13:21:41 2024 -0400 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f88b18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/cmake-build-debug +/.idea diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b99144f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.18) +project(ReTexture + VERSION 1.0 + LANGUAGES CXX +) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +# Enable Package Managers +include(cmake/CPM.cmake) + +set(CMAKE_CXX_STANDARD 20) + +file(GLOB_RECURSE HEADERS "include/*.h") +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(COPY "testImages" DESTINATION "${PROJECT_BINARY_DIR}") + +find_package(PNG REQUIRED) + +include_directories("include" "/usr/include/libpng16") + +if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + message(FATAL_ERROR "In-Source builds are not allowed") +endif() + +add_library(ReTexture SHARED ${SOURCES} ${HEADERS}) +add_executable(ReTextureTest main.cpp) + +set_target_properties(ReTexture PROPERTIES LINKER_LANGUAGE CXX) +set_target_properties(ReTextureTest PROPERTIES LINKER_LANGUAGE CXX) + +target_link_libraries(ReTexture PUBLIC PNG::PNG) +target_link_libraries(ReTextureTest PUBLIC ReTexture) diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake new file mode 100644 index 0000000..d866ad7 --- /dev/null +++ b/cmake/CPM.cmake @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT +# +# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors + +set(CPM_DOWNLOAD_VERSION 0.38.7) +set(CPM_HASH_SUM "83e5eb71b2bbb8b1f2ad38f1950287a057624e385c238f6087f94cdfc44af9c5") + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) + +include(${CPM_DOWNLOAD_LOCATION}) \ No newline at end of file diff --git a/include/ReTexture/flags.h b/include/ReTexture/flags.h new file mode 100644 index 0000000..bb6fed8 --- /dev/null +++ b/include/ReTexture/flags.h @@ -0,0 +1,11 @@ +#pragma once + +enum class RTextureFlag : unsigned int { + INVERT_Y = 0, + INVERT_X = 1, +}; + +enum class RTextureFormat : bool { + RGB = false, + RGBA = true +}; \ No newline at end of file diff --git a/include/ReTexture/rTexture.h b/include/ReTexture/rTexture.h new file mode 100644 index 0000000..9d63dbb --- /dev/null +++ b/include/ReTexture/rTexture.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include + +class RTexture { +private: + void load(const std::string& file); + void loadBMP(const std::string& file); + void loadPNG(const std::string& file); + void invertY(); +public: + uint width; + uint height; + RTextureFormat format; + std::vector pixelData; + RTexture(const std::string& file, const std::vector& args); +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..2661fc5 --- /dev/null +++ b/main.cpp @@ -0,0 +1,26 @@ +#include +#include + +int main() { + auto* bmp = new RTexture("testImages/1.bmp", {RTextureFlag::INVERT_Y}); + std::cout << "Bitmap Width: " << bmp->width << std::endl; + std::cout << "Bitmap Height: " << bmp->height << std::endl; + + if (bmp->format == RTextureFormat::RGB) + std::cout << "Bitmap Format: RGB" << std::endl; + + if (bmp->format == RTextureFormat::RGBA) + std::cout << "Bitmap Format: RGBA" << std::endl; + delete bmp; + + auto* png = new RTexture("testImages/1.png", {RTextureFlag::INVERT_Y}); + std::cout << "PNG width: " << png->width << std::endl; + std::cout << "PNG height: " << png->height << std::endl; + + if (png->format == RTextureFormat::RGB) + std::cout << "PNG Format: RGB" << std::endl; + + if (png->format == RTextureFormat::RGBA) + std::cout << "PNG Format: RGBA" << std::endl; + delete png; +} diff --git a/src/rTexture.cpp b/src/rTexture.cpp new file mode 100644 index 0000000..4b0dfea --- /dev/null +++ b/src/rTexture.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#include +void RTexture::load(const std::string& file) { + std::ifstream ifStream(file, std::ios::in | std::ios::binary); + unsigned char bmpFileHeader[14]; + + ifStream.read(reinterpret_cast(bmpFileHeader), 14); + if (bmpFileHeader[0] == 'B' && bmpFileHeader[1] == 'M') { + ifStream.close(); + loadBMP(file); + return; + } + + //TODO + if (file.ends_with(".png")) + loadPNG(file); +} + +void RTexture::loadBMP(const std::string& file) { + std::ifstream bmpFile(file, std::ios::in | std::ios::binary); + if (!bmpFile.is_open()) + return; + + unsigned char bmpFileHeader[14]; + unsigned char bmpInfoHeader[40]; + + bmpFile.read(reinterpret_cast(bmpFileHeader), 14); + bmpFile.read(reinterpret_cast(bmpInfoHeader), 40); + + width = bmpInfoHeader[4] + (bmpInfoHeader[5] << 8) + (bmpInfoHeader[6] << 16) + (bmpInfoHeader[7] << 24); + height = bmpInfoHeader[8] + (bmpInfoHeader[9] << 8) + (bmpInfoHeader[10] << 16) + (bmpInfoHeader[11] << 24); + + int rowPadded = (width * 3 + 3) & (~3); + pixelData.resize(width * height * 3); + + std::vector rowData(rowPadded); + for (int y = height - 1; y >= 0; --y) { + bmpFile.read(reinterpret_cast(rowData.data()), rowPadded); + for (int x = 0; x < width; ++x) { + pixelData[(y * width + x) * 3 + 2] = rowData[x * 3 + 0]; + pixelData[(y * width + x) * 3 + 1] = rowData[x * 3 + 1]; + pixelData[(y * width + x) * 3 + 0] = rowData[x * 3 + 2]; + } + } + bmpFile.close(); + format = RTextureFormat::RGB; +} + +void RTexture::loadPNG(const std::string &file) { + FILE *fp = fopen(file.c_str(), "rb"); + if (!fp) { + fprintf(stderr, "Could not open file %s for reading\n", file.c_str()); + return; + } + + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png) { + fprintf(stderr, "Could not create png read struct\n"); + fclose(fp); + return; + } + + png_infop info = png_create_info_struct(png); + if (!info) { + fprintf(stderr, "Could not create png info struct\n"); + png_destroy_read_struct(&png, nullptr, nullptr); + fclose(fp); + return; + } + + if (setjmp(png_jmpbuf(png))) { + fprintf(stderr, "Error during png creation\n"); + png_destroy_read_struct(&png, &info, nullptr); + fclose(fp); + return; + } + + png_init_io(png, fp); + png_read_info(png, info); + + width = png_get_image_width(png, info); + height = png_get_image_height(png, info); + png_byte color_type = png_get_color_type(png, info); + png_byte bit_depth = png_get_bit_depth(png, info); + + if (bit_depth == 16) + png_set_strip_16(png); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png); + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8(png); + + if (png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png); + + if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) + png_set_filler(png, 0xFF, PNG_FILLER_AFTER); + + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png); + + png_read_update_info(png, info); + + pixelData.resize(png_get_rowbytes(png, info) * height); + std::vector row_pointers(height); + + for (unsigned y = 0; y < height; ++y) { + row_pointers[y] = pixelData.data() + y * png_get_rowbytes(png, info); + } + + png_read_image(png, row_pointers.data()); + + fclose(fp); + png_destroy_read_struct(&png, &info, nullptr); + if (color_type & PNG_COLOR_MASK_ALPHA) + format = RTextureFormat::RGBA; + + if (!(color_type & PNG_COLOR_MASK_ALPHA)) + format = RTextureFormat::RGB; +} + +RTexture::RTexture(const std::string &file, const std::vector &args) { + load(file); + if (std::find(args.begin(), args.end(), RTextureFlag::INVERT_Y) != args.end()) + invertY(); +} + +void RTexture::invertY() { + unsigned int rowSize; + if (format == RTextureFormat::RGB) + rowSize = width * 3; + if (format == RTextureFormat::RGBA) + rowSize = width * 4; + + std::vector temp(rowSize); + for (uint y = 0; y < height / 2; ++y) { + unsigned char* topRow = &pixelData[y * rowSize]; + unsigned char* bottomRow = &pixelData[(height - y - 1) * rowSize]; + std::copy(bottomRow, bottomRow + rowSize, temp.begin()); + std::copy(topRow, topRow + rowSize, bottomRow); + std::copy(temp.begin(), temp.end(), topRow); + } +} diff --git a/testImages/1.bmp b/testImages/1.bmp new file mode 100644 index 0000000..6f3936b Binary files /dev/null and b/testImages/1.bmp differ diff --git a/testImages/1.png b/testImages/1.png new file mode 100644 index 0000000..f242e8d Binary files /dev/null and b/testImages/1.png differ