/* 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