Skip to content
Closed
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
123 changes: 53 additions & 70 deletions source/gfx/JPEGTexture.cpp
Original file line number Diff line number Diff line change
@@ -1,90 +1,73 @@
#include "JPEGTexture.h"
#include "utils/logger.h"
#include <cstdlib>
#include <cstring>
#include <gx2/mem.h>
#include <stdexcept>
#include <turbojpeg.h>

GX2Texture *JPEG_LoadTexture(std::span<uint8_t> data) {
GX2Texture *texture = nullptr;
int height;
int width;
using namespace std::literals;

tjhandle handle = tj3Init(TJINIT_DECOMPRESS);
if (!handle) {
goto error;
}
struct TJError : std::runtime_error {
TJError(tjhandle handle) : std::runtime_error{tj3GetErrorStr(handle)} {}
};

if (tj3DecompressHeader(handle, data.data(), data.size())) {
DEBUG_FUNCTION_LINE_ERR("Failed to parse JPEG header: %s\n", tj3GetErrorStr(handle));
goto error;
// RAII wrapper for tjhandle
class JPEGImage {
public:
JPEGImage() : mHandle{tj3Init(TJINIT_DECOMPRESS)} {
if (!mHandle) {
throw TJError{mHandle};
}
}

width = tj3Get(handle, TJPARAM_JPEGWIDTH);
height = tj3Get(handle, TJPARAM_JPEGHEIGHT);
if (width == -1 || height == -1) {
DEBUG_FUNCTION_LINE_ERR("Unknown JPEG image size\n");
goto error;
~JPEGImage() noexcept {
if (mHandle)
tj3Destroy(mHandle);
}

texture = static_cast<GX2Texture *>(std::malloc(sizeof(GX2Texture)));
if (!texture) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate texture\n");
goto error;
void decompressHeader(std::span<const uint8_t> data) {
if (tj3DecompressHeader(mHandle, data.data(), data.size())) {
throw TJError{mHandle};
}
}

std::memset(texture, 0, sizeof(GX2Texture));
texture->surface.width = width;
texture->surface.height = height;
texture->surface.depth = 1;
texture->surface.mipLevels = 1;
texture->surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
texture->surface.aa = GX2_AA_MODE1X;
texture->surface.use = GX2_SURFACE_USE_TEXTURE;
texture->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D;
texture->surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED;
texture->surface.swizzle = 0;
texture->viewFirstMip = 0;
texture->viewNumMips = 1;
texture->viewFirstSlice = 0;
texture->viewNumSlices = 1;
texture->compMap = 0x0010203;
GX2CalcSurfaceSizeAndAlignment(&texture->surface);
GX2InitTextureRegs(texture);

if (texture->surface.imageSize == 0) {
DEBUG_FUNCTION_LINE_ERR("Texture is empty\n");
goto error;
void decompress8(std::span<const uint8_t> data, void *dst,
std::uint32_t rowStride, int pixelFormat) {
if (tj3Decompress8(mHandle,
data.data(), data.size(),
static_cast<unsigned char *>(dst),
rowStride,
pixelFormat)) {
throw TJError{mHandle};
}
}

texture->surface.image = std::aligned_alloc(texture->surface.alignment,
texture->surface.imageSize);
if (!texture->surface.image) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate surface for texture\n");
goto error;
std::uint32_t getWidth() const {
int width = tj3Get(mHandle, TJPARAM_JPEGWIDTH);
if (width < 0) {
throw std::runtime_error{"Unknown JPEG width"};
}
return width;
}

if (tj3Decompress8(handle,
data.data(), data.size(),
static_cast<unsigned char *>(texture->surface.image),
texture->surface.pitch * 4,
TJPF_RGBA)) {
DEBUG_FUNCTION_LINE_ERR("Failed to read JPEG image: %s\n", tj3GetErrorStr(handle));
goto error;
std::uint32_t getHeight() const {
int height = tj3Get(mHandle, TJPARAM_JPEGHEIGHT);
if (height < 0) {
throw std::runtime_error{"Unknown JPEG height"};
}
return height;
}

tj3Destroy(handle);

GX2Invalidate(GX2_INVALIDATE_MODE_CPU | GX2_INVALIDATE_MODE_TEXTURE,
texture->surface.image, texture->surface.imageSize);

return texture;
private:
tjhandle mHandle = nullptr;
}; // class JPEGImage

error:
if (texture) {
std::free(texture->surface.image);
std::expected<Texture, std::string> JPEG_LoadTexture(std::span<const uint8_t> data) noexcept {
try {
JPEGImage jpeg;
jpeg.decompressHeader(data);
Texture texture{jpeg.getWidth(), jpeg.getHeight()};
jpeg.decompress8(data, texture.getPixels(), texture.getRowStride(), TJPF_RGBA);
texture.flush();
return texture;
} catch (std::exception &e) {
return std::unexpected{"[JPEGTexture] "s + e.what()};
}
std::free(texture);
tj3Destroy(handle);
return nullptr;
}
6 changes: 4 additions & 2 deletions source/gfx/JPEGTexture.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#pragma once

#include "Texture.h"
#include <cstdint>
#include <gx2/texture.h>
#include <expected>
#include <span>
#include <string>

GX2Texture *JPEG_LoadTexture(std::span<uint8_t> data);
std::expected<Texture, std::string> JPEG_LoadTexture(std::span<const uint8_t> data) noexcept;
103 changes: 41 additions & 62 deletions source/gfx/PNGTexture.cpp
Original file line number Diff line number Diff line change
@@ -1,79 +1,58 @@
#include "PNGTexture.h"
#include "utils/logger.h"
#include <cstdlib>
#include <cstring>
#include <gx2/mem.h>
#include <png.h>
#include <stdexcept>

GX2Texture *PNG_LoadTexture(std::span<uint8_t> data) {
GX2Texture *texture = nullptr;
using namespace std::literals;

png_image image{};
image.version = PNG_IMAGE_VERSION;

if (!png_image_begin_read_from_memory(&image, data.data(), data.size())) {
DEBUG_FUNCTION_LINE_ERR("Failed to parse PNG header: %s\n", image.message);
goto error;
// RAII wrapper for png_image
class PNGImage {
public:
PNGImage() {
mImage.version = PNG_IMAGE_VERSION;
}

// Request the output to always be RGBA
image.format = PNG_FORMAT_RGBA;

texture = static_cast<GX2Texture *>(std::malloc(sizeof(GX2Texture)));
if (!texture) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate texture\n");
goto error;
~PNGImage() noexcept {
png_image_free(&mImage);
}

std::memset(texture, 0, sizeof(GX2Texture));
texture->surface.width = image.width;
texture->surface.height = image.height;
texture->surface.depth = 1;
texture->surface.mipLevels = 1;
texture->surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
texture->surface.aa = GX2_AA_MODE1X;
texture->surface.use = GX2_SURFACE_USE_TEXTURE;
texture->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D;
texture->surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED;
texture->surface.swizzle = 0;
texture->viewFirstMip = 0;
texture->viewNumMips = 1;
texture->viewFirstSlice = 0;
texture->viewNumSlices = 1;
texture->compMap = 0x0010203;
GX2CalcSurfaceSizeAndAlignment(&texture->surface);
GX2InitTextureRegs(texture);

if (texture->surface.imageSize == 0) {
DEBUG_FUNCTION_LINE_ERR("Texture is empty\n");
goto error;
void beginRead(std::span<const uint8_t> data) {
if (!png_image_begin_read_from_memory(&mImage, data.data(), data.size())) {
throw std::runtime_error{"Failed to parse PNG header: "s + mImage.message};
}
}

texture->surface.image = std::aligned_alloc(texture->surface.alignment,
texture->surface.imageSize);
if (!texture->surface.image) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate surface for texture\n");
goto error;
void finishRead(png_const_colorp background, void *dst,
png_int_32 rowStride, void *colormap) {
// Request the output to always be RGBA
mImage.format = PNG_FORMAT_RGBA;
if (!png_image_finish_read(&mImage, background, dst, rowStride, nullptr)) {
throw std::runtime_error{"Failed to read PNG image: "s + mImage.message};
}
}

if (!png_image_finish_read(&image, nullptr,
texture->surface.image,
texture->surface.pitch * 4,
nullptr)) {
DEBUG_FUNCTION_LINE_ERR("Failed to read PNG image: %s\n", image.message);
goto error;
std::uint32_t
getWidth() const noexcept {
return mImage.width;
}

GX2Invalidate(GX2_INVALIDATE_MODE_CPU | GX2_INVALIDATE_MODE_TEXTURE,
texture->surface.image, texture->surface.imageSize);

return texture;
std::uint32_t getHeight() const noexcept {
return mImage.height;
}

error:
if (texture) {
std::free(texture->surface.image);
private:
png_image mImage{};

}; // class PNGImage

std::expected<Texture, std::string> PNG_LoadTexture(std::span<const uint8_t> data) noexcept {
try {
PNGImage png;
png.beginRead(data);
Texture texture{png.getWidth(), png.getHeight()};
png.finishRead(nullptr, texture.getPixels(), texture.getRowStride(), nullptr);
texture.flush();
return texture;
} catch (std::exception &e) {
return std::unexpected{"[PNGTexture] "s + e.what()};
}
std::free(texture);
png_image_free(&image);
return nullptr;
}
6 changes: 4 additions & 2 deletions source/gfx/PNGTexture.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#pragma once

#include "Texture.h"
#include <cstdint>
#include <gx2/texture.h>
#include <expected>
#include <span>
#include <string>

GX2Texture *PNG_LoadTexture(std::span<uint8_t> data);
std::expected<Texture, std::string> PNG_LoadTexture(std::span<const uint8_t> data) noexcept;
Loading
Loading