Skip to content
This repository has been archived by the owner on Oct 4, 2022. It is now read-only.

Add support for JPEG format (#79) #107

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions PoltergeistEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
add_library(PoltergeistEngine STATIC src/PoltergeistEngine/Rendering/VertexArray.cpp src/PoltergeistEngine/Rendering/IndexBuffer.cpp src/PoltergeistEngine/Rendering/VertexBuffer.cpp src/PoltergeistEngine/Rendering/Shader.cpp src/PoltergeistEngine/Rendering/ShaderStage.cpp src/PoltergeistEngine/Rendering/Texture.cpp src/PoltergeistEngine/Image/Image.cpp src/PoltergeistEngine/Rendering/Renderer.cpp src/PoltergeistEngine/Encoding/EncodingUtilities.cpp src/PoltergeistEngine/IO/FileUtilities.cpp src/PoltergeistEngine/Rendering/FrameBuffer.cpp)
add_library(PoltergeistEngine STATIC src/PoltergeistEngine/Rendering/VertexArray.cpp src/PoltergeistEngine/Rendering/IndexBuffer.cpp src/PoltergeistEngine/Rendering/VertexBuffer.cpp src/PoltergeistEngine/Rendering/Shader.cpp src/PoltergeistEngine/Rendering/ShaderStage.cpp src/PoltergeistEngine/Rendering/Texture.cpp src/PoltergeistEngine/Image/Image.cpp src/PoltergeistEngine/Image/JpegImage.cpp src/PoltergeistEngine/Image/PngImage.cpp src/PoltergeistEngine/Rendering/Renderer.cpp src/PoltergeistEngine/Encoding/EncodingUtilities.cpp src/PoltergeistEngine/IO/FileUtilities.cpp src/PoltergeistEngine/Rendering/FrameBuffer.cpp)
target_include_directories(PoltergeistEngine PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/")
find_package(glad CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(libpng CONFIG REQUIRED)
target_link_libraries(PoltergeistEngine PRIVATE glad::glad glm::glm png)
find_package(JPEG REQUIRED)
target_link_libraries(PoltergeistEngine PRIVATE glad::glad glm::glm png JPEG::JPEG)
add_library(Poltergeist::PoltergeistEngine ALIAS PoltergeistEngine)
13 changes: 6 additions & 7 deletions PoltergeistEngine/include/PoltergeistEngine/Image/Image.hpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
#ifndef POLTERGEIST_IMAGE_HPP
#ifndef POLTERGEIST_IMAGE_HPP
#define POLTERGEIST_IMAGE_HPP

#include <cstdint>
#include <filesystem>

class Image
{
private:
protected:
uint32_t m_width = 0, m_height = 0;
uint8_t* m_data = nullptr;
public:
explicit Image(const std::filesystem::path &imagePath);
~Image() noexcept;
[[nodiscard]] static std::shared_ptr<Image> LoadFromFile(const std::filesystem::path& imagePath);

uint32_t GetWidth() const noexcept;
uint32_t GetHeight() const noexcept;
uint8_t* GetData() const noexcept;
[[nodiscard]] uint32_t GetWidth() const noexcept;
[[nodiscard]] uint32_t GetHeight() const noexcept;
[[nodiscard]] uint8_t* GetData() const noexcept;
};

#endif
13 changes: 13 additions & 0 deletions PoltergeistEngine/include/PoltergeistEngine/Image/JpegImage.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef POLTERGEIST_JPEGIMAGE_HPP
#define POLTERGEIST_JPEGIMAGE_HPP

#include "PoltergeistEngine/Image/Image.hpp"

class JpegImage : public Image
{
public:
[[nodiscard]] static std::shared_ptr<JpegImage> LoadFromFile(FILE* file);
[[nodiscard]] static bool IsValidHeader(FILE* file);
};

#endif
13 changes: 13 additions & 0 deletions PoltergeistEngine/include/PoltergeistEngine/Image/PngImage.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef POLTERGEIST_PNGIMAGE_HPP
#define POLTERGEIST_PNGIMAGE_HPP

#include "PoltergeistEngine/Image/Image.hpp"

class PngImage : public Image
{
public:
[[nodiscard]] static std::shared_ptr<PngImage> LoadFromFile(FILE* file);
[[nodiscard]] static bool IsValidHeader(FILE* file);
};

#endif
65 changes: 14 additions & 51 deletions PoltergeistEngine/src/PoltergeistEngine/Image/Image.cpp
Original file line number Diff line number Diff line change
@@ -1,66 +1,29 @@
#include "PoltergeistEngine/Image/Image.hpp"
#include "PoltergeistEngine/Image/Image.hpp"
#include "PoltergeistEngine/Image/PngImage.hpp"
#include "PoltergeistEngine/Image/JpegImage.hpp"
#include "PoltergeistEngine/IO/FileUtilities.hpp"
#include <cstdio>
#include <png.h>

Image::Image(const std::filesystem::path &imagePath)
std::shared_ptr<Image> Image::LoadFromFile(const std::filesystem::path& imagePath)
{
FILE* file = OpenFile(imagePath.generic_string().c_str(), "rb");
FILE* file = OpenFile(reinterpret_cast<const char*>(imagePath.u8string().c_str()), "rb");

if (!file)
throw std::runtime_error("Couldn't open the file");

uint8_t header[8];
fread(header, 1, 8, file);
fseek(file, -8, SEEK_CUR);

if (png_sig_cmp(header, 0, 8) != 0)
throw std::runtime_error("Unsupported format");

png_structp internalState = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);

png_set_user_limits(internalState, 0x7fffffff, 0x7fffffff);

png_infop imageInfo = png_create_info_struct(internalState);

png_init_io(internalState, file);
png_read_info(internalState, imageInfo);
png_set_palette_to_rgb(internalState);

m_width = png_get_image_width(internalState, imageInfo);
m_height = png_get_image_height(internalState, imageInfo);

png_read_update_info(internalState, imageInfo);

png_bytepp rows = new png_bytep[m_height];

for (size_t row = 0; row < m_height; row++)
std::shared_ptr<Image> result;
if (JpegImage::IsValidHeader(file))
result = JpegImage::LoadFromFile(file);
if (PngImage::IsValidHeader(file))
result = PngImage::LoadFromFile(file);
else
{
rows[row] = new png_byte[m_width * 3];
fclose(file);
throw std::runtime_error("Unsupported format!");
}

png_read_image(internalState, rows);
png_read_end(internalState, imageInfo);

m_data = new uint8_t[m_width * m_height * 3];

for (uint32_t y = 0; y < m_height; y++)
{
for (uint32_t x = 0; x < m_width * 3; x++)
{
m_data[y * m_width * 3 + x] = rows[y][x];
}
}

delete[] rows;

png_destroy_read_struct(&internalState, &imageInfo, nullptr);
fclose(file);
}

Image::~Image() noexcept
{
delete[] m_data;
return result;
}

uint32_t Image::GetWidth() const noexcept
Expand Down
75 changes: 75 additions & 0 deletions PoltergeistEngine/src/PoltergeistEngine/Image/JpegImage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "PoltergeistEngine/Image/JpegImage.hpp"
#include <jpeglib.h>
#include <iostream>
#include <csetjmp>
#include <cstring>

struct ErrorManager
{
jpeg_error_mgr publicErrorManager;
jmp_buf setJumpBuffer;
};

void ErrorExit(j_common_ptr decompressInfo)
{
ErrorManager* errorManager = reinterpret_cast<ErrorManager*>(decompressInfo->err);
(*decompressInfo->err->output_message) (decompressInfo);
longjmp(errorManager->setJumpBuffer, 1);
}

bool JpegImage::IsValidHeader(FILE* file)
{
uint8_t header[2];
size_t readSize = fread(&header, 1, 2, file);
fseek(file, -readSize, SEEK_CUR);

if (readSize != 2)
return false;

return header[0] == 0xFF && header[1] == 0xD8;
}

std::shared_ptr<JpegImage> JpegImage::LoadFromFile(FILE* file)
{
jpeg_decompress_struct decompressInfo;
decompressInfo.out_color_space = JCS_RGB;
ErrorManager errorManager;
decompressInfo.err = jpeg_std_error(&errorManager.publicErrorManager);
errorManager.publicErrorManager.error_exit = ErrorExit;
if (setjmp(errorManager.setJumpBuffer))
{
jpeg_destroy_decompress(&decompressInfo);
throw std::runtime_error("Initializing decompress error");
}

jpeg_create_decompress(&decompressInfo);
jpeg_stdio_src(&decompressInfo, file);
jpeg_read_header(&decompressInfo, true);
jpeg_start_decompress(&decompressInfo);

int rowLength = decompressInfo.output_width * decompressInfo.output_components;
JSAMPARRAY pixelBuffer;
pixelBuffer = (*decompressInfo.mem->alloc_sarray)
(reinterpret_cast<j_common_ptr>(&decompressInfo), JPOOL_IMAGE, rowLength, 1);

std::shared_ptr<JpegImage> decompressResult = std::make_shared<JpegImage>();
decompressResult->m_data = new uint8_t[decompressInfo.output_width * decompressInfo.output_height * 3];
size_t dataOffset = 0;
while (decompressInfo.output_scanline < decompressInfo.output_height)
{
jpeg_read_scanlines(&decompressInfo, pixelBuffer, 1);
memcpy(decompressResult->m_data + dataOffset, pixelBuffer[0], rowLength);
dataOffset += rowLength;
}

jpeg_finish_decompress(&decompressInfo);
jpeg_destroy_decompress(&decompressInfo);

if (errorManager.publicErrorManager.num_warnings > 0)
throw std::runtime_error("Decompressing error");

decompressResult->m_width = decompressInfo.output_width;
decompressResult->m_height = decompressInfo.output_height;

return decompressResult;
}
62 changes: 62 additions & 0 deletions PoltergeistEngine/src/PoltergeistEngine/Image/PngImage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include "PoltergeistEngine/Image/PngImage.hpp"
#include <png.h>

bool PngImage::IsValidHeader(FILE* file)
{
uint8_t header[8];
size_t readSize = fread(header, 1, 8, file);
fseek(file, -readSize, SEEK_CUR);

if (readSize != 8)
return false;

return png_sig_cmp(header, 0, 8) == 0;
}

std::shared_ptr<PngImage> PngImage::LoadFromFile(FILE* file)
{
png_structp internalState = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);

png_set_user_limits(internalState, 0x7fffffff, 0x7fffffff);

png_infop imageInfo = png_create_info_struct(internalState);

png_init_io(internalState, file);
png_read_info(internalState, imageInfo);
png_set_palette_to_rgb(internalState);

std::shared_ptr<PngImage> decompressResult = std::make_shared<PngImage>();
decompressResult->m_width = png_get_image_width(internalState, imageInfo);
decompressResult->m_height = png_get_image_height(internalState, imageInfo);

png_read_update_info(internalState, imageInfo);

png_bytepp rows = new png_bytep[decompressResult->m_height];

for (size_t row = 0; row < decompressResult->m_height; row++)
{
rows[row] = new png_byte[decompressResult->m_width * 3];
}

png_read_image(internalState, rows);
png_read_end(internalState, imageInfo);

decompressResult->m_data = new uint8_t[decompressResult->m_width * decompressResult->m_height * 3];

for (uint32_t y = 0; y < decompressResult->m_height; y++)
{
for (uint32_t x = 0; x < decompressResult->m_width * 3; x++)
{
decompressResult->m_data[y * decompressResult->m_width * 3 + x] = rows[y][x];
}
}

for (size_t row = 0; row < decompressResult->m_height; row++)
{
delete[] rows[row];
}

png_destroy_read_struct(&internalState, &imageInfo, nullptr);

return decompressResult;
}
6 changes: 3 additions & 3 deletions PoltergeistEngine/src/PoltergeistEngine/Rendering/Texture.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "PoltergeistEngine/Rendering/Texture.hpp"
#include "PoltergeistEngine/Rendering/Texture.hpp"
#include "PoltergeistEngine/Image/Image.hpp"
#include <glad/glad.h>

Expand Down Expand Up @@ -46,10 +46,10 @@ std::shared_ptr<Texture> Texture::CreateFromFile(const std::filesystem::path &te
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

Image image(texturePath);
std::shared_ptr<Image> image = Image::LoadFromFile(texturePath);

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.GetWidth(), image.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, image.GetData());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->GetWidth(), image->GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, image->GetData());

texture->Unbind();

Expand Down
1 change: 1 addition & 0 deletions vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"glad",
"glm",
"libpng",
"libjpeg-turbo",
{
"name":"imgui",
"features":[
Expand Down