/*
Copyright 2014 Michele "King_DuckZ" Santullo
This file is part of CloonelJump.
CloonelJump is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CloonelJump is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CloonelJump. If not, see .
*/
#include "texture.hpp"
#include "sdlerror.hpp"
#include "sdlmain.hpp"
#include "physicsfswrapper.hpp"
#include
#include
#include
#include
#include
#include
#include
#define lengthof(a) (static_cast(sizeof(a) / sizeof(a[0])))
namespace cloonel {
namespace {
enum GraphicFormat {
GraphicFormat_Unknown,
GraphicFormat_Png
};
enum ColorChannelMask {
#if __BYTE_ORDER == __BIG_ENDIAN
ColorChannelMask_Red = 0xff000000,
ColorChannelMask_Green = 0xff0000,
ColorChannelMask_Blue = 0xff00,
ColorChannelMask_Alpha = 0xff
#elif __BYTE_ORDER == __LITTLE_ENDIAN
ColorChannelMask_Red = 0xff,
ColorChannelMask_Green = 0xff00,
ColorChannelMask_Blue = 0xff0000,
ColorChannelMask_Alpha = 0xff000000
#else
# error "Unknonwn endianness"
#endif
};
struct GraphicFormatItem {
const char* extension;
size_t length;
GraphicFormat enumvalue;
};
const GraphicFormatItem g_graphicFormatItems[] = {
{".png", 4, GraphicFormat_Png}
};
GraphicFormat GuessGraphicFormatFromName (const std::string& parPath) __attribute__((pure));
///----------------------------------------------------------------------
///----------------------------------------------------------------------
SDL_Surface* SurfaceFromPngRGBA (ushort2 parSize, int parBpp, const png_structp& parPngPtr, const png_infop& parInfoPtr, std::vector& parBuff) {
const png_size_t stride = png_get_rowbytes(parPngPtr, parInfoPtr);
assert(stride > 0);
parBuff.resize(stride * parSize.y());
uint8_t* const imagePtr = parBuff.data();
for (uint16_t y = 0; y < parSize.y(); ++y) {
png_read_row(parPngPtr, imagePtr + stride * y, nullptr);
}
SDL_Surface* const retSurf = SDL_CreateRGBSurfaceFrom(
imagePtr,
parSize.x(),
parSize.y(),
parBpp,
static_cast(stride),
ColorChannelMask_Red,
ColorChannelMask_Green,
ColorChannelMask_Blue,
ColorChannelMask_Alpha
);
return retSurf;
}
///----------------------------------------------------------------------
///----------------------------------------------------------------------
GraphicFormat GuessGraphicFormatFromName (const std::string& parPath) {
const size_t dotPos = parPath.find_last_of('.');
if (parPath.npos == dotPos) {
return GraphicFormat_Unknown;
}
const size_t extLen = parPath.size() - dotPos;
for (size_t z = 0; z < sizeof(g_graphicFormatItems) / sizeof(g_graphicFormatItems[0]); ++z) {
const GraphicFormatItem& currItem = g_graphicFormatItems[z];
if (currItem.length == extLen and parPath.compare(dotPos, currItem.length, currItem.extension) == 0) {
return currItem.enumvalue;
}
}
return GraphicFormat_Unknown;
}
///----------------------------------------------------------------------
///----------------------------------------------------------------------
void ReadDataFromInputStream (png_structp parPngPtr, png_bytep parOutBytes, png_size_t parByteCountToRead) {
if (not png_get_io_ptr(parPngPtr))
return;
PhysicsFSFile& rawfile = *static_cast(png_get_io_ptr(parPngPtr));
const int64_t read = rawfile.Read(static_cast(parOutBytes), static_cast(parByteCountToRead), 1);
if (read != static_cast(parByteCountToRead))
return;
return;
}
///----------------------------------------------------------------------
///http://blog.hammerian.net/2009/reading-png-images-from-memory/
///----------------------------------------------------------------------
SDL_Surface* LoadNewPngSurface (const std::string& parPath, std::vector& parBuff) {
PhysicsFSFile rawfile(parPath.c_str(), PhysicsFSFile::OpenMode_Read, "graphics");
unsigned char header[8];
assert(rawfile.IsOpen());
//Read the signature from the input stream
rawfile.Read(header, lengthof(header), 1);
if (png_sig_cmp(header, 0, lengthof(header)))
return nullptr;
//Get the file info struct
png_structp pngptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (not pngptr)
return nullptr;
//Get the data info struct
png_infop infoptr = png_create_info_struct(pngptr);
if (not infoptr) {
png_destroy_read_struct(&pngptr, nullptr, nullptr);
return nullptr;
}
//Set the function that will be called to read data from the stream
png_set_read_fn(pngptr, &rawfile, ReadDataFromInputStream);
//Tell the lib we already verified the signature
png_set_sig_bytes(pngptr, lengthof(header));
//Read the header
png_uint_32 width, height;
int bitDepth, colorType;
png_read_info(pngptr, infoptr);
const png_uint_32 headerGetInfoRetVal = png_get_IHDR(pngptr, infoptr, &width, &height, &bitDepth, &colorType, nullptr, nullptr, nullptr);
if (static_cast(-1) == headerGetInfoRetVal) {
png_destroy_read_struct(&pngptr, &infoptr, nullptr);
return nullptr;
}
SDL_Surface* retSurf;
switch (colorType) {
case PNG_COLOR_TYPE_RGB:
retSurf = nullptr; //SurfaceFromPngRGB();
assert(false); //not implemented
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
retSurf = SurfaceFromPngRGBA(ushort2(static_cast(width), static_cast(height)), bitDepth * 4, pngptr, infoptr, parBuff);
break;
default:
png_destroy_read_struct(&pngptr, &infoptr, nullptr);
retSurf = nullptr;
}
return retSurf;
}
} //unnamed namespace
///--------------------------------------------------------------------------
///--------------------------------------------------------------------------
Texture::Texture (const std::string& parPath, SDLMain* parMain, bool parLoadNow) :
m_path(parPath),
m_texture(nullptr),
m_sdlmain(parMain)
{
if (parLoadNow)
Reload();
}
///--------------------------------------------------------------------------
///--------------------------------------------------------------------------
Texture::~Texture() noexcept {
Destroy();
}
///--------------------------------------------------------------------------
///--------------------------------------------------------------------------
void Texture::Destroy() noexcept {
if (m_texture) {
SDL_DestroyTexture(m_texture);
m_texture = nullptr;
}
}
///--------------------------------------------------------------------------
///--------------------------------------------------------------------------
void Texture::Reload() {
const GraphicFormat fmt = GuessGraphicFormatFromName(m_path);
Destroy();
SDL_Surface* surf = nullptr;
std::vector memBuff;
switch (fmt) {
case GraphicFormat_Png:
surf = LoadNewPngSurface(m_path.c_str(), memBuff);
break;
default:
throw std::runtime_error(std::string("Unsupported file format for \"") + m_path + "\"");
}
if (nullptr == surf)
throw std::runtime_error(GetFullErrorMessage(__PRETTY_FUNCTION__, m_path));
m_texture = SDL_CreateTextureFromSurface(m_sdlmain->GetRenderer(), surf);
SDL_FreeSurface(surf);
if (m_texture) {
int width, height;
SDL_QueryTexture(m_texture, nullptr, nullptr, &width, &height);
m_size = static_cast(int2(width, height));
}
}
///--------------------------------------------------------------------------
///--------------------------------------------------------------------------
void Texture::Render (int2 parPos, ushort2 parSize) const {
assert(IsLoaded());
const int screenHeight = m_sdlmain->DefWidthHeight().y();
const SDL_Rect dest = { parPos.x(), screenHeight - parPos.y() - parSize.y(), parSize.x(), parSize.y() };
SDL_RenderCopy(m_sdlmain->GetRenderer(), m_texture, nullptr, &dest);
}
} //namespace cloonel