2014-02-25 11:04:16 +01:00
|
|
|
/*
|
|
|
|
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/>.
|
|
|
|
*/
|
|
|
|
|
2014-02-08 23:11:26 +01:00
|
|
|
#include "texture.hpp"
|
|
|
|
#include "sdlerror.hpp"
|
|
|
|
#include "sdlmain.hpp"
|
2014-02-13 00:01:29 +01:00
|
|
|
#include "physicsfswrapper.hpp"
|
2014-08-14 12:00:04 +02:00
|
|
|
#include "compatibility.h"
|
2014-08-14 12:54:18 +02:00
|
|
|
#include "casts.hpp"
|
2014-02-08 23:11:26 +01:00
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
#include <stdexcept>
|
2014-02-09 00:32:11 +01:00
|
|
|
#include <cassert>
|
2014-02-13 00:01:29 +01:00
|
|
|
#include <ciso646>
|
2014-02-21 21:28:34 +01:00
|
|
|
#include <png.h>
|
2014-02-21 22:31:16 +01:00
|
|
|
#include <endian.h>
|
|
|
|
#include <vector>
|
2016-11-09 22:25:57 +01:00
|
|
|
#if !defined(NDEBUG)
|
|
|
|
# include <cmath>
|
|
|
|
#endif
|
2014-02-08 23:11:26 +01:00
|
|
|
|
2014-02-22 12:26:26 +01:00
|
|
|
#define lengthof(a) (static_cast<int32_t>(sizeof(a) / sizeof(a[0])))
|
2014-02-21 21:51:56 +01:00
|
|
|
|
2014-02-08 23:11:26 +01:00
|
|
|
namespace cloonel {
|
2014-02-13 00:01:29 +01:00
|
|
|
namespace {
|
2014-02-21 21:28:34 +01:00
|
|
|
enum GraphicFormat {
|
|
|
|
GraphicFormat_Unknown,
|
|
|
|
GraphicFormat_Png
|
|
|
|
};
|
2014-02-21 21:51:56 +01:00
|
|
|
|
2014-03-06 10:55:14 +01:00
|
|
|
enum PixelFormat {
|
|
|
|
PixelFormat_RGB_24,
|
|
|
|
PixelFormat_RGBA_32
|
|
|
|
};
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
enum ColorChannelMask {
|
2014-02-21 22:31:16 +01:00
|
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
2014-02-21 21:51:56 +01:00
|
|
|
ColorChannelMask_Red = 0xff000000,
|
|
|
|
ColorChannelMask_Green = 0xff0000,
|
|
|
|
ColorChannelMask_Blue = 0xff00,
|
|
|
|
ColorChannelMask_Alpha = 0xff
|
2014-02-21 22:31:16 +01:00
|
|
|
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
2014-02-21 21:51:56 +01:00
|
|
|
ColorChannelMask_Red = 0xff,
|
|
|
|
ColorChannelMask_Green = 0xff00,
|
|
|
|
ColorChannelMask_Blue = 0xff0000,
|
|
|
|
ColorChannelMask_Alpha = 0xff000000
|
|
|
|
#else
|
|
|
|
# error "Unknonwn endianness"
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2014-02-21 21:28:34 +01:00
|
|
|
struct GraphicFormatItem {
|
|
|
|
const char* extension;
|
|
|
|
size_t length;
|
|
|
|
GraphicFormat enumvalue;
|
|
|
|
};
|
|
|
|
|
2014-03-24 21:32:24 +01:00
|
|
|
struct RectFloat {
|
|
|
|
float2 from;
|
|
|
|
float2 to;
|
|
|
|
|
|
|
|
RectFloat ( void ) = default;
|
|
|
|
RectFloat ( const RectFloat& parOther ) :
|
|
|
|
from(parOther.from),
|
|
|
|
to(parOther.to)
|
|
|
|
{
|
|
|
|
assert(IsValid());
|
|
|
|
}
|
|
|
|
|
|
|
|
RectFloat ( const SDL_Rect& parRect ) :
|
|
|
|
from(static_cast<float>(parRect.x), static_cast<float>(parRect.y)),
|
|
|
|
to(static_cast<float>(parRect.x + parRect.w), static_cast<float>(parRect.y + parRect.h))
|
|
|
|
{
|
|
|
|
assert(IsValid());
|
|
|
|
}
|
|
|
|
|
|
|
|
RectFloat ( const float2& parFrom, const float2& parTo ) :
|
|
|
|
from(parFrom),
|
|
|
|
to(parTo)
|
|
|
|
{
|
|
|
|
assert(IsValid());
|
|
|
|
}
|
|
|
|
|
|
|
|
~RectFloat ( void ) noexcept = default;
|
|
|
|
|
|
|
|
operator SDL_Rect ( void ) const noexcept { return { static_cast<int>(from.x()), static_cast<int>(from.y()), static_cast<int>(Width()), static_cast<int>(Height()) }; }
|
|
|
|
|
|
|
|
float Width ( void ) const noexcept { return to.x() - from.x(); }
|
|
|
|
float Height ( void ) const noexcept { return to.y() - from.y(); }
|
|
|
|
float2 WidthHeight ( void ) const noexcept { return float2(Width(), Height()); }
|
|
|
|
bool IsValid ( void ) const noexcept { return from <= to; }
|
|
|
|
};
|
|
|
|
|
2014-02-21 21:28:34 +01:00
|
|
|
const GraphicFormatItem g_graphicFormatItems[] = {
|
|
|
|
{".png", 4, GraphicFormat_Png}
|
|
|
|
};
|
|
|
|
|
2014-08-14 12:00:04 +02:00
|
|
|
GraphicFormat GuessGraphicFormatFromName (const std::string& parPath) a_pure;
|
2014-03-24 21:32:24 +01:00
|
|
|
bool ClipRect ( RectFloat& parSrc, RectFloat& parDest, const RectFloat& parClip );
|
2014-03-25 10:32:34 +01:00
|
|
|
#if !defined(NDEBUG)
|
2014-08-14 12:00:04 +02:00
|
|
|
bool IsRectCompletelyInsideRect ( const RectFloat& parInner, const RectFloat& parOuter ) a_pure;
|
2014-03-25 10:32:34 +01:00
|
|
|
#endif
|
2014-02-21 21:28:34 +01:00
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
///----------------------------------------------------------------------
|
|
|
|
///----------------------------------------------------------------------
|
2014-03-06 10:55:14 +01:00
|
|
|
SDL_Surface* SurfaceFromPng (PixelFormat parPixelFormat, ushort2 parSize, const png_structp& parPngPtr, const png_infop& parInfoPtr, std::vector<uint8_t>& parBuff) {
|
2014-02-21 21:51:56 +01:00
|
|
|
const png_size_t stride = png_get_rowbytes(parPngPtr, parInfoPtr);
|
|
|
|
assert(stride > 0);
|
|
|
|
|
2014-02-21 22:31:16 +01:00
|
|
|
parBuff.resize(stride * parSize.y());
|
|
|
|
uint8_t* const imagePtr = parBuff.data();
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
for (uint16_t y = 0; y < parSize.y(); ++y) {
|
|
|
|
png_read_row(parPngPtr, imagePtr + stride * y, nullptr);
|
|
|
|
}
|
|
|
|
|
2014-03-06 10:55:14 +01:00
|
|
|
int bpp;
|
|
|
|
uint32_t redMask, greenMask, blueMask, alphaMask;
|
|
|
|
switch (parPixelFormat) {
|
|
|
|
case PixelFormat_RGB_24:
|
|
|
|
bpp = 24;
|
|
|
|
redMask = ColorChannelMask_Red;
|
|
|
|
greenMask = ColorChannelMask_Green;
|
|
|
|
blueMask = ColorChannelMask_Blue;
|
|
|
|
alphaMask = 0;
|
|
|
|
break;
|
|
|
|
case PixelFormat_RGBA_32:
|
|
|
|
bpp = 32;
|
|
|
|
redMask = ColorChannelMask_Red;
|
|
|
|
greenMask = ColorChannelMask_Green;
|
|
|
|
blueMask = ColorChannelMask_Blue;
|
|
|
|
alphaMask = ColorChannelMask_Alpha;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
SDL_Surface* const retSurf = SDL_CreateRGBSurfaceFrom(
|
|
|
|
imagePtr,
|
|
|
|
parSize.x(),
|
|
|
|
parSize.y(),
|
2014-03-06 10:55:14 +01:00
|
|
|
bpp,
|
2014-08-14 12:54:18 +02:00
|
|
|
checked_numcast<int>(stride),
|
2014-03-06 10:55:14 +01:00
|
|
|
redMask,
|
|
|
|
greenMask,
|
|
|
|
blueMask,
|
|
|
|
alphaMask
|
2014-02-21 21:51:56 +01:00
|
|
|
);
|
|
|
|
return retSurf;
|
|
|
|
}
|
|
|
|
|
|
|
|
///----------------------------------------------------------------------
|
|
|
|
///----------------------------------------------------------------------
|
2014-02-21 21:28:34 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
///----------------------------------------------------------------------
|
|
|
|
///----------------------------------------------------------------------
|
|
|
|
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));
|
|
|
|
|
2014-08-14 12:54:18 +02:00
|
|
|
const int64_t read = rawfile.Read(static_cast<void*>(parOutBytes), checked_numcast<uint32_t>(parByteCountToRead), 1);
|
2014-02-21 21:51:56 +01:00
|
|
|
if (read != static_cast<int64_t>(parByteCountToRead))
|
|
|
|
return;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
///----------------------------------------------------------------------
|
|
|
|
///http://blog.hammerian.net/2009/reading-png-images-from-memory/
|
|
|
|
///----------------------------------------------------------------------
|
2014-02-21 22:31:16 +01:00
|
|
|
SDL_Surface* LoadNewPngSurface (const std::string& parPath, std::vector<uint8_t>& parBuff) {
|
2014-02-21 21:28:34 +01:00
|
|
|
PhysicsFSFile rawfile(parPath.c_str(), PhysicsFSFile::OpenMode_Read, "graphics");
|
|
|
|
unsigned char header[8];
|
|
|
|
assert(rawfile.IsOpen());
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
//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);
|
2014-02-21 21:28:34 +01:00
|
|
|
return nullptr;
|
2014-02-21 21:51:56 +01:00
|
|
|
}
|
2014-02-21 21:28:34 +01:00
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
SDL_Surface* retSurf;
|
|
|
|
switch (colorType) {
|
|
|
|
case PNG_COLOR_TYPE_RGB:
|
2014-03-06 10:55:14 +01:00
|
|
|
assert(3 * bitDepth == 24);
|
|
|
|
retSurf = SurfaceFromPng(PixelFormat_RGB_24, ushort2(static_cast<uint8_t>(width), static_cast<uint8_t>(height)), pngptr, infoptr, parBuff);
|
2014-02-21 21:51:56 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
2014-03-06 10:55:14 +01:00
|
|
|
assert(bitDepth * 4 == 32);
|
|
|
|
retSurf = SurfaceFromPng(PixelFormat_RGBA_32, ushort2(static_cast<uint8_t>(width), static_cast<uint8_t>(height)), pngptr, infoptr, parBuff);
|
2014-02-21 21:51:56 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
retSurf = nullptr;
|
|
|
|
}
|
2014-02-21 21:28:34 +01:00
|
|
|
|
2016-10-31 20:09:44 +01:00
|
|
|
png_destroy_read_struct(&pngptr, &infoptr, nullptr);
|
2014-02-21 21:51:56 +01:00
|
|
|
return retSurf;
|
2014-02-13 00:01:29 +01:00
|
|
|
}
|
2014-03-24 21:32:24 +01:00
|
|
|
|
|
|
|
///----------------------------------------------------------------------
|
|
|
|
///Adjustes parSrc and parDest so that parDest will fit into parClip and
|
|
|
|
///parSrc to be the relevant part of the source texture to be drawn.
|
|
|
|
///Returns true if such operation was possible and parSrc and parDest
|
|
|
|
///got set to a valid value, otherwise false.
|
|
|
|
///----------------------------------------------------------------------
|
|
|
|
bool ClipRect (RectFloat& parSrc, RectFloat& parDest, const RectFloat& parClip) {
|
|
|
|
assert(parSrc.IsValid());
|
|
|
|
assert(parDest.IsValid());
|
|
|
|
|
|
|
|
//If the dest rect is completely out of the clipping region, there
|
|
|
|
//is nothing to do at all.
|
|
|
|
if (parDest.to <= parClip.from or parDest.from >= parClip.to)
|
|
|
|
return false;
|
|
|
|
|
2016-11-09 22:25:57 +01:00
|
|
|
RectFloat dst;
|
|
|
|
dst.from.x() = std::max(parDest.from.x(), parClip.from.x());
|
|
|
|
dst.from.y() = std::max(parDest.from.y(), parClip.from.y());
|
|
|
|
dst.to.x() = std::min(parDest.to.x(), parClip.to.x());
|
|
|
|
dst.to.y() = std::min(parDest.to.y(), parClip.to.y());
|
2014-03-24 21:32:24 +01:00
|
|
|
|
|
|
|
if (not dst.IsValid())
|
|
|
|
return false;
|
|
|
|
|
2016-11-09 22:25:57 +01:00
|
|
|
assert(parDest.from <= dst.from);
|
|
|
|
assert(parDest.to >= dst.to);
|
2014-03-24 21:32:24 +01:00
|
|
|
|
2016-11-09 22:25:57 +01:00
|
|
|
RectFloat src;
|
|
|
|
{
|
|
|
|
const float2 srcWidthHeight(parSrc.WidthHeight());
|
|
|
|
const float2 scaledOffs((dst.from - parDest.from) / parDest.WidthHeight());
|
|
|
|
src.from = parSrc.from + scaledOffs * srcWidthHeight;
|
|
|
|
const float2 scaledCrop((parDest.to - dst.to) / parDest.WidthHeight());
|
|
|
|
src.to = parSrc.to - scaledCrop * srcWidthHeight;
|
|
|
|
assert(src.IsValid());
|
|
|
|
|
|
|
|
assert(dst.IsValid());
|
|
|
|
assert(dst.from >= parClip.from);
|
|
|
|
assert(dst.to <= parClip.to);
|
|
|
|
}
|
|
|
|
parDest = dst;
|
|
|
|
parSrc = src;
|
2014-03-24 21:32:24 +01:00
|
|
|
return true;
|
|
|
|
}
|
2014-03-25 10:32:34 +01:00
|
|
|
|
|
|
|
#if !defined(NDEBUG)
|
|
|
|
///----------------------------------------------------------------------
|
|
|
|
///----------------------------------------------------------------------
|
|
|
|
bool IsRectCompletelyInsideRect (const RectFloat& parInner, const RectFloat& parOuter) {
|
|
|
|
return parInner.from >= parOuter.from and parInner.to <= parOuter.to;
|
|
|
|
}
|
|
|
|
#endif
|
2014-02-13 00:01:29 +01:00
|
|
|
} //unnamed namespace
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
///--------------------------------------------------------------------------
|
|
|
|
///--------------------------------------------------------------------------
|
2014-02-08 23:11:26 +01:00
|
|
|
Texture::Texture (const std::string& parPath, SDLMain* parMain, bool parLoadNow) :
|
|
|
|
m_path(parPath),
|
|
|
|
m_texture(nullptr),
|
|
|
|
m_sdlmain(parMain)
|
|
|
|
{
|
|
|
|
if (parLoadNow)
|
|
|
|
Reload();
|
|
|
|
}
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
///--------------------------------------------------------------------------
|
|
|
|
///--------------------------------------------------------------------------
|
2014-02-08 23:11:26 +01:00
|
|
|
Texture::~Texture() noexcept {
|
|
|
|
Destroy();
|
|
|
|
}
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
///--------------------------------------------------------------------------
|
|
|
|
///--------------------------------------------------------------------------
|
2014-02-08 23:11:26 +01:00
|
|
|
void Texture::Destroy() noexcept {
|
|
|
|
if (m_texture) {
|
|
|
|
SDL_DestroyTexture(m_texture);
|
|
|
|
m_texture = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
///--------------------------------------------------------------------------
|
|
|
|
///--------------------------------------------------------------------------
|
2014-02-08 23:11:26 +01:00
|
|
|
void Texture::Reload() {
|
2014-02-21 21:28:34 +01:00
|
|
|
const GraphicFormat fmt = GuessGraphicFormatFromName(m_path);
|
2014-02-08 23:11:26 +01:00
|
|
|
Destroy();
|
2014-02-21 21:28:34 +01:00
|
|
|
|
|
|
|
SDL_Surface* surf = nullptr;
|
2014-02-21 22:31:16 +01:00
|
|
|
std::vector<uint8_t> memBuff;
|
2014-02-21 21:28:34 +01:00
|
|
|
switch (fmt) {
|
|
|
|
case GraphicFormat_Png:
|
2014-02-21 22:31:16 +01:00
|
|
|
surf = LoadNewPngSurface(m_path.c_str(), memBuff);
|
2014-02-21 21:51:56 +01:00
|
|
|
break;
|
2014-02-21 21:28:34 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
throw std::runtime_error(std::string("Unsupported file format for \"") + m_path + "\"");
|
|
|
|
}
|
|
|
|
|
2014-02-08 23:11:26 +01:00
|
|
|
if (nullptr == surf)
|
|
|
|
throw std::runtime_error(GetFullErrorMessage(__PRETTY_FUNCTION__, m_path));
|
|
|
|
|
|
|
|
m_texture = SDL_CreateTextureFromSurface(m_sdlmain->GetRenderer(), surf);
|
2014-02-09 00:32:11 +01:00
|
|
|
SDL_FreeSurface(surf);
|
2014-02-10 01:20:16 +01:00
|
|
|
if (m_texture) {
|
2014-03-12 11:04:22 +01:00
|
|
|
int2 wh;
|
|
|
|
SDL_QueryTexture(m_texture, nullptr, nullptr, &wh.x(), &wh.y());
|
2016-11-02 18:31:59 +01:00
|
|
|
m_size = vector_cast<float2>(wh);
|
2014-02-10 01:20:16 +01:00
|
|
|
}
|
2014-02-09 00:32:11 +01:00
|
|
|
}
|
|
|
|
|
2014-02-21 21:51:56 +01:00
|
|
|
///--------------------------------------------------------------------------
|
|
|
|
///--------------------------------------------------------------------------
|
2014-03-25 10:32:34 +01:00
|
|
|
void Texture::Render (const float2& parPos, const float2& parSize, const float2& parScaling, bool parClip) const {
|
2014-02-09 00:32:11 +01:00
|
|
|
assert(IsLoaded());
|
2014-03-24 21:32:24 +01:00
|
|
|
|
|
|
|
const float2 pos(parPos * parScaling);
|
|
|
|
const float2 siz(parSize * parScaling);
|
|
|
|
RectFloat dest(pos, pos + siz);
|
|
|
|
RectFloat src(float2(0.0f), m_size);
|
|
|
|
|
2014-03-25 10:32:34 +01:00
|
|
|
if (parClip) {
|
2016-11-02 18:31:59 +01:00
|
|
|
const RectFloat clip(float2(0.0f), vector_cast<float2>(m_sdlmain->WidthHeight()));
|
2014-03-25 10:32:34 +01:00
|
|
|
const bool visible = ClipRect(src, dest, clip);
|
|
|
|
if (not visible)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#if !defined(NDEBUG)
|
|
|
|
else {
|
2016-11-02 18:31:59 +01:00
|
|
|
const RectFloat clip(float2(0.0f), vector_cast<float2>(m_sdlmain->WidthHeight()));
|
2014-03-25 10:32:34 +01:00
|
|
|
assert(IsRectCompletelyInsideRect(dest, clip));
|
|
|
|
}
|
|
|
|
#endif
|
2014-03-24 21:32:24 +01:00
|
|
|
|
|
|
|
const SDL_Rect sdlsrc(src);
|
|
|
|
SDL_Rect sdldst(dest);
|
|
|
|
sdldst.y = m_sdlmain->WidthHeight().y() - sdldst.y - sdldst.h;
|
|
|
|
|
|
|
|
//By here everything is nice and clean
|
|
|
|
assert(sdlsrc.x >= 0 and sdlsrc.y >= 0);
|
|
|
|
assert(sdlsrc.w >= 0 and sdlsrc.w <= static_cast<int>(m_size.x()));
|
|
|
|
assert(sdlsrc.h >= 0 and sdlsrc.h <= static_cast<int>(m_size.y()));
|
|
|
|
assert(sdlsrc.x + sdlsrc.w <= m_sdlmain->WidthHeight().x());
|
|
|
|
assert(sdlsrc.y + sdlsrc.h <= m_sdlmain->WidthHeight().y());
|
|
|
|
SDL_RenderCopy(m_sdlmain->GetRenderer(), m_texture, &sdlsrc, &sdldst);
|
2014-02-08 23:11:26 +01:00
|
|
|
}
|
|
|
|
} //namespace cloonel
|