251 lines
8.3 KiB
C++
251 lines
8.3 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "texture.hpp"
|
|
#include "sdlerror.hpp"
|
|
#include "sdlmain.hpp"
|
|
#include "physicsfswrapper.hpp"
|
|
#include <SDL2/SDL.h>
|
|
#include <stdexcept>
|
|
#include <cassert>
|
|
#include <ciso646>
|
|
#include <png.h>
|
|
#include <endian.h>
|
|
#include <vector>
|
|
|
|
#define lengthof(a) (static_cast<int32_t>(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<uint8_t>& 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<int>(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<PhysicsFSFile*>(png_get_io_ptr(parPngPtr));
|
|
|
|
const int64_t read = rawfile.Read(static_cast<void*>(parOutBytes), static_cast<uint32_t>(parByteCountToRead), 1);
|
|
if (read != static_cast<int64_t>(parByteCountToRead))
|
|
return;
|
|
|
|
return;
|
|
}
|
|
|
|
///----------------------------------------------------------------------
|
|
///http://blog.hammerian.net/2009/reading-png-images-from-memory/
|
|
///----------------------------------------------------------------------
|
|
SDL_Surface* LoadNewPngSurface (const std::string& parPath, std::vector<uint8_t>& 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<png_uint_32>(-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<uint8_t>(width), static_cast<uint8_t>(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<uint8_t> 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<ushort2>(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
|