MyCurry/src/gamelib/drawing_queue.cpp

179 lines
5.9 KiB
C++

/*
Copyright 2016, 2017 Michele "King_DuckZ" Santullo
This file is part of MyCurry.
MyCurry 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.
MyCurry 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 MyCurry. If not, see <http://www.gnu.org/licenses/>.
*/
#include "drawing_queue.hpp"
#include "sdlmain.hpp"
#include "sizeratio.hpp"
#include "rect_to_sdl.hpp"
#include "texture.hpp"
#include <SDL2/SDL.h>
#include <cassert>
#include <ciso646>
#include <utility>
#include <vector>
#include <boost/container/flat_map.hpp>
#include <cmath>
namespace curry {
namespace {
typedef std::vector<DrawingQueue::ItemInfo> TextureList;
typedef std::vector<TextureList> LayerList;
typedef boost::container::flat_map<uint16_t, std::size_t> LayerIdxMap;
#if !defined(NDEBUG)
bool are_equal_rel (float parA, float parB, float parEpsilon) a_pure;
//see: http://stackoverflow.com/questions/4548004/how-to-correctly-and-standardly-compare-floats
bool are_equal_rel (float parA, float parB, float parEpsilon) {
return (std::fabs(parA - parB) <= parEpsilon * std::max(std::fabs(parA), std::fabs(parB)));
}
#endif
///----------------------------------------------------------------------
///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 clip_rect (Rect<float>& parSrc, Rect<float>& parDest, const Rect<float>& parClip) {
typedef Rect<float> RectFloat;
assert(are_equal_rel(parSrc.width(), parDest.width(), 0.00001f));
assert(parSrc.is_valid());
assert(parDest.is_valid());
//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;
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());
if (not dst.is_valid())
return false;
assert(parDest.from <= dst.from);
assert(parDest.to >= dst.to);
RectFloat src;
{
const vec2f srcWidthHeight(parSrc.width_height());
const vec2f scaledOffs((dst.from - parDest.from) / parDest.width_height());
src.from = parSrc.from + scaledOffs * srcWidthHeight;
const vec2f scaledCrop((parDest.to - dst.to) / parDest.width_height());
src.to = parSrc.to - scaledCrop * srcWidthHeight;
assert(src.is_valid());
assert(dst.is_valid());
assert(dst.from >= parClip.from);
assert(dst.to <= parClip.to);
assert(are_equal_rel(src.width(), dst.width(), 0.00001f));
}
parDest = dst;
parSrc = src;
return true;
}
} //unnamed namespace
DrawingQueue::ItemInfo::ItemInfo() = default;
DrawingQueue::ItemInfo::ItemInfo (ItemInfo&&) = default;
DrawingQueue::ItemInfo::~ItemInfo() noexcept = default;
DrawingQueue::ItemInfo& DrawingQueue::ItemInfo::operator=(ItemInfo&&) = default;
struct DrawingQueue::LocalData {
explicit LocalData (cloonel::SDLMain* parSDLMain) :
sdlmain(parSDLMain),
screen_res(0.0f),
def_layer_id(0)
{
}
LayerList layers;
LayerIdxMap layer_idx_map;
cloonel::SDLMain* sdlmain;
vec2f screen_res;
std::size_t def_layer_id;
};
DrawingQueue::DrawingQueue (cloonel::SDLMain* parSDLMain, const cloonel::DeferredRegister& parDeferredRegister) :
SizeNotifiableBase(parSDLMain, parDeferredRegister),
m_local_data(new LocalData(parSDLMain))
{
assert(parSDLMain);
}
DrawingQueue::~DrawingQueue() noexcept = default;
bool DrawingQueue::add_layer (uint16_t parName) {
auto& idx_map = m_local_data->layer_idx_map;
auto it_found = idx_map.lower_bound(parName);
if (it_found != idx_map.end() and it_found->first == parName) {
return false;
}
else {
m_local_data->layers.push_back(TextureList());
const auto new_layer_index = m_local_data->layers.size() - 1;
idx_map.insert(it_found, std::make_pair(parName, new_layer_index));
return true;
}
}
void DrawingQueue::flush_to_renderer() {
assert(not m_local_data->layers.empty());
for (auto& layer : m_local_data->layers) {
for (auto& itm : layer) {
draw_clipped(*itm.texture, itm.source, itm.destination);
}
layer.clear();
}
}
void DrawingQueue::add_for_rendering (uint16_t parName, ItemInfo&& parItem) {
const auto idx = m_local_data->layer_idx_map.at(parName);
auto& layers = m_local_data->layers;
assert(idx < layers.size());
assert(parItem.source.is_valid());
assert(parItem.destination.is_valid());
assert(parItem.texture);
layers[idx].push_back(std::move(parItem));
assert(not layers[idx].empty());
assert(layers[idx].back().source.is_valid());
assert(layers[idx].back().destination.is_valid());
}
void DrawingQueue::draw_clipped (Texture& parTexture, Rect<float> parSrc, Rect<float> parDest) {
const vec2f lefttop(0.0f);
const vec2f& rightbottom = m_local_data->screen_res;
clip_rect(parSrc, parDest, Rect<float>(lefttop, rightbottom));
auto src = make_sdlrect(parSrc);
auto dst = make_sdlrect(parDest);
assert(src.w == dst.w);
assert(src.h == dst.h);
SDL_RenderCopy(m_local_data->sdlmain->GetRenderer(), parTexture.texture(), &src, &dst);
}
void DrawingQueue::OnResChanged (const cloonel::SizeRatio& parSizeRatio) {
m_local_data->screen_res = vec2f(cl_vec2us(parSizeRatio.Resolution()));
}
} //namespace curry