convert between glm::vec and sb::Color, print hierarchy on config access error, composite text onto a separate background surface, set frame time to zero when timer paused
This commit is contained in:
parent
6cb9688bec
commit
55d6e08480
|
@ -9,7 +9,7 @@ void Animation::play(float delay, bool play_once)
|
|||
{
|
||||
this->delay = delay;
|
||||
play_state = true;
|
||||
paused = false;
|
||||
unpause();
|
||||
previous_step_time = timer.elapsed();
|
||||
overflow = 0;
|
||||
ending = play_once;
|
||||
|
|
|
@ -154,7 +154,7 @@ public:
|
|||
bool collide(const glm::vec2& point) const;
|
||||
|
||||
/*!
|
||||
* Collide a line segment with the box. If intersection is passed and there is a collision, intersection will be filled with
|
||||
* Collide a line segment with the box. If intrseection is passed and there is a collision, intersection will be filled with
|
||||
* the coordinates of the first intersection found unless the segment is fully within the box not touching any edges.
|
||||
*
|
||||
* @param segment line segment to collide with the box
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <ostream>
|
||||
#include <type_traits>
|
||||
#include "SDL_pixels.h"
|
||||
#include "glm/glm.hpp"
|
||||
|
||||
struct Color : SDL_Color
|
||||
{
|
||||
|
@ -24,6 +25,24 @@ public:
|
|||
|
||||
Color();
|
||||
Color(const SDL_Color&);
|
||||
|
||||
template<glm::length_t dimensions, typename Type, glm::qualifier qualifier>
|
||||
Color(glm::vec<dimensions, Type, qualifier> vector)
|
||||
{
|
||||
if constexpr (dimensions == 2)
|
||||
{
|
||||
*this = Color(vector.x, vector.y, 0.0f);
|
||||
}
|
||||
else if constexpr (dimensions == 3)
|
||||
{
|
||||
*this = Color(vector.x, vector.y, vector.z);
|
||||
}
|
||||
else if constexpr (dimensions == 4)
|
||||
{
|
||||
*this = Color(vector.x, vector.y, vector.z, vector.w);
|
||||
}
|
||||
}
|
||||
|
||||
void percent(float, float, float);
|
||||
void percent(float, float, float, float);
|
||||
void hsv(float, float = 1.0f, float = 1.0f);
|
||||
|
@ -52,6 +71,26 @@ public:
|
|||
a = static_cast<int>(alpha) & 255;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert sb::Color to glm::vec of 3 or 4 dimensions. If vector is 3-dimensional, return only the RGB portion
|
||||
* of the color.
|
||||
*/
|
||||
template<glm::length_t dimensions, typename Type, glm::qualifier qualifier>
|
||||
operator glm::vec<dimensions, Type, qualifier>() const
|
||||
{
|
||||
if constexpr (dimensions == 3)
|
||||
{
|
||||
return glm::vec3 {r, g, b};
|
||||
}
|
||||
else if constexpr (dimensions == 4)
|
||||
{
|
||||
return glm::vec4 {r, g, b, a};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Cannot convert color the glm::vec of dimensions other than 3 or 4");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void RGBtoHSV(const float&, const float&, const float&, float&, float&, float&);
|
||||
|
|
|
@ -42,20 +42,34 @@ private:
|
|||
* @warning This probably should not be called directly and is just used to provide a recursive call for variadic
|
||||
* access through Configuration::operator()
|
||||
*
|
||||
* @param json a JSON element in the configuration
|
||||
* @param key first-level key
|
||||
* @param keys zero or more next level keys
|
||||
* @param hierarchy string that appends the key as a string to the end as the function runs recursively to represent the hierarchy
|
||||
* in case of an error
|
||||
* @param json a JSON element in the configuration
|
||||
* @param key first-level key
|
||||
* @param keys zero or more next level keys
|
||||
*/
|
||||
template<class Key, class... Keys>
|
||||
const nlohmann::json& access(const nlohmann::json& json, const Key& key, const Keys&... keys) const
|
||||
const nlohmann::json& access(std::ostringstream& hierarchy, const nlohmann::json& json, const Key& key, const Keys&... keys) const
|
||||
{
|
||||
hierarchy << '"' << key << '"';
|
||||
if constexpr (sizeof...(keys) > 0)
|
||||
{
|
||||
return access(json[key], keys...);
|
||||
hierarchy << " > ";
|
||||
return access(hierarchy, json[key], keys...);
|
||||
}
|
||||
else
|
||||
{
|
||||
return json[key];
|
||||
hierarchy << ")";
|
||||
try
|
||||
{
|
||||
return json.at(key);
|
||||
}
|
||||
catch (const std::exception& exception)
|
||||
{
|
||||
std::ostringstream message;
|
||||
message << "Error accessing configuration at " << hierarchy.str() << ": " << exception.what();
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,9 +109,9 @@ public:
|
|||
const nlohmann::json& operator()() const;
|
||||
|
||||
/*!
|
||||
* Get a read-only reference to the value at the given hierarchy of keys. If no type is specified for the return type, a
|
||||
* JSON object will be returned.
|
||||
*
|
||||
* Get a read-only reference to the JSON object at the given hierarchy of keys. The key must exist in the configuration, otherwise an exception
|
||||
* will be thrown. To get a reference to JSON object for a new key, use sb::Configuration::operator[](const std::string) instead.
|
||||
*
|
||||
* Example,
|
||||
*
|
||||
* std::cout << _configuration() << std::endl;
|
||||
|
@ -109,13 +123,15 @@ public:
|
|||
* true {"any-key-ignore-commands":[],"default-unsuppress-delay":0.7,"ignore-repeat-keypress":true,"suppress-any-key-on-mods":true, \
|
||||
* "system-any-key-ignore-commands":["fullscreen","screenshot","record","quit"]} 261.0
|
||||
*
|
||||
* @param keys hierarchy of keys used to look up a specific value in the config JSON
|
||||
* @return read-only reference to value specified by keys
|
||||
* @param keys hierarchy of keys used to look up a specific JSON object in the config
|
||||
* @return read-only reference to JSON object specified by keys
|
||||
*/
|
||||
template<typename... Key>
|
||||
const nlohmann::json& operator()(const Key&... keys) const
|
||||
{
|
||||
return access(config, keys...);
|
||||
std::ostringstream hierarchy;
|
||||
hierarchy << "(";
|
||||
return access(hierarchy, config, keys...);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -75,6 +75,14 @@ public:
|
|||
|
||||
};
|
||||
|
||||
/* Add Input class to the sb namespace. This should be the default location, but Input is left in the global namespace
|
||||
* for backward compatibility.
|
||||
*/
|
||||
namespace sb
|
||||
{
|
||||
using ::Input;
|
||||
}
|
||||
|
||||
#include "extension.hpp"
|
||||
#include "Configuration.hpp"
|
||||
#include "Delegate.hpp"
|
||||
|
|
90
src/Text.cpp
90
src/Text.cpp
|
@ -2,74 +2,98 @@
|
|||
|
||||
using namespace sb;
|
||||
|
||||
/*!
|
||||
* @param content text to be displayed
|
||||
*/
|
||||
void Text::content(const std::string& content)
|
||||
{
|
||||
_content = content;
|
||||
refresh();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param foreground text color
|
||||
*/
|
||||
void Text::foreground(const sb::Color& foreground)
|
||||
{
|
||||
_foreground = foreground;
|
||||
refresh();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param background text background color which fills the Plane
|
||||
*/
|
||||
void Text::background(const sb::Color& background)
|
||||
{
|
||||
_background = background;
|
||||
refresh();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param font shared pointer to a TTF_Font
|
||||
*/
|
||||
void Text::font(std::shared_ptr<TTF_Font> font)
|
||||
{
|
||||
_font = font;
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Text::size(const glm::vec2 &size)
|
||||
{
|
||||
_size = size;
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Text::scaling_quality(GLint quality)
|
||||
{
|
||||
_scaling_quality = quality;
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Text::refresh()
|
||||
{
|
||||
/* Create pixel data using the SDL image library. The pixel data surface is converted from paletted format to ARGB and flipped. */
|
||||
SDL_Surface* shaded = TTF_RenderText_Shaded(_font.get(), _content.c_str(), _foreground, _background);
|
||||
if (!shaded)
|
||||
/* Render the text with transparent background as RGBA pixel data using the SDL image library. */
|
||||
std::shared_ptr<SDL_Surface> blended {TTF_RenderText_Blended(_font.get(), _content.c_str(), _foreground), SDL_FreeSurface};
|
||||
if (!blended)
|
||||
{
|
||||
sb::Log::sdl_error("Could not create text");
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_Surface* converted = converted = SDL_ConvertSurfaceFormat(shaded, SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
if (!converted)
|
||||
/* Use the size of the rendered text unless size has been set. */
|
||||
glm::vec2 size;
|
||||
if (_size.has_value())
|
||||
{
|
||||
sb::Log::sdl_error("Could not convert pixel format");
|
||||
size = _size.value();
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_Surface* flipped = rotozoomSurfaceXY(converted, 0, 1, -1, 0);
|
||||
if (!flipped)
|
||||
{
|
||||
sb::Log::sdl_error("Could not flip surface");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Generate texture and create storage. Load the pixels from the text rendering surface into the default texture. The texture object will handle
|
||||
* destroying the previous texture. Then destroy the pixels surface. */
|
||||
texture().generate({flipped->w, flipped->h}, GL_RGBA8, GL_LINEAR);
|
||||
texture().load(flipped);
|
||||
SDL_FreeSurface(flipped);
|
||||
}
|
||||
SDL_FreeSurface(converted);
|
||||
size = glm::vec2{blended->w, blended->h};
|
||||
}
|
||||
|
||||
/* Create a container surface with the same format as the rendered text that the rendered text will be composited onto. */
|
||||
std::shared_ptr<SDL_Surface> container
|
||||
{
|
||||
SDL_CreateRGBSurfaceWithFormat(0, size.x, size.y, blended->format->BitsPerPixel, blended->format->format),
|
||||
SDL_FreeSurface
|
||||
};
|
||||
|
||||
if (!container)
|
||||
{
|
||||
sb::Log::sdl_error("Could not create container surface for rendered text");
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_FillRect(container.get(), nullptr, _background);
|
||||
sb::Box blended_box {0.0f, 0.0f, float(blended->w), float(blended->h), false};
|
||||
sb::Box container_box {0.0f, 0.0f, float(container->w), float(container->h), false};
|
||||
blended_box.center(container_box.center());
|
||||
SDL_Rect r = blended_box;
|
||||
SDL_BlitSurface(blended.get(), nullptr, container.get(), &r);
|
||||
blended = container;
|
||||
}
|
||||
|
||||
/* Rotate and mirror the surface for compatibility with OpenGL */
|
||||
std::shared_ptr<SDL_Surface> flipped {rotozoomSurfaceXY(blended.get(), 0, 1, -1, 0), SDL_FreeSurface};
|
||||
|
||||
if (!flipped)
|
||||
{
|
||||
sb::Log::sdl_error("Could not flip surface");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Generate texture and create storage. Load pixels from the text rendering surface into the texture. The texture object will handle
|
||||
* destroying the previous texture. */
|
||||
texture().generate({flipped->w, flipped->h}, GL_RGBA8, _scaling_quality);
|
||||
texture().load(flipped.get());
|
||||
}
|
||||
SDL_FreeSurface(shaded);
|
||||
}
|
||||
}
|
||||
|
|
27
src/Text.hpp
27
src/Text.hpp
|
@ -1,3 +1,5 @@
|
|||
#include <optional>
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_ttf.h"
|
||||
#include "sdl2-gfx/SDL2_rotozoom.h"
|
||||
|
@ -18,6 +20,8 @@ namespace sb
|
|||
std::string _content;
|
||||
sb::Color _foreground, _background;
|
||||
std::shared_ptr<TTF_Font> _font;
|
||||
std::optional<glm::vec2> _size;
|
||||
GLint _scaling_quality = GL_LINEAR;
|
||||
|
||||
/*!
|
||||
* Load the texture with the appropriate SDL_Surface pixels created by the SDL TTF library.
|
||||
|
@ -27,17 +31,24 @@ namespace sb
|
|||
public:
|
||||
|
||||
/*!
|
||||
* Construct a sb::Text object from a font, string, and background and foreground colors.
|
||||
* Construct a sb::Text object from a font, string, and background and foreground colors. A texture with the text rendered in the
|
||||
* given colors will be created and attached to this object.
|
||||
*
|
||||
* The font must be wrapped in a shared pointer. A default loaded font is available from Game::font().
|
||||
*
|
||||
* A size can be given in pixels, in which case the text will be rendered at the center of a texture in exactly the given dimensions,
|
||||
* regardless of the length of the text. If the given dimensions are larger than the text, the resulting texture will have padding around
|
||||
* the text. This can be useful, for example, for creating identically sized text buttons.
|
||||
*
|
||||
* @param font a TTF_Font object wrapped in a shared pointer
|
||||
* @param content text content
|
||||
* @param foreground text color
|
||||
* @param background background color
|
||||
* @param size force the texture to be a certain size in pixels, extra area is filled with the background color
|
||||
*/
|
||||
Text(std::shared_ptr<TTF_Font> font, const std::string& content = "", const sb::Color& foreground = DEFAULT_FG,
|
||||
const sb::Color& background = DEFAULT_BG) : _content(content), _foreground(foreground), _background(background), _font(font)
|
||||
const sb::Color& background = DEFAULT_BG, std::optional<glm::vec2> size = std::nullopt) :
|
||||
_content(content), _foreground(foreground), _background(background), _font(font), _size(size)
|
||||
{
|
||||
/* Add an empty Texture object */
|
||||
texture(sb::Texture());
|
||||
|
@ -62,6 +73,18 @@ namespace sb
|
|||
* @param font shared pointer to a TTF_Font
|
||||
*/
|
||||
void font(std::shared_ptr<TTF_Font> font);
|
||||
|
||||
/*!
|
||||
* @param size force the texture to be a certain size in pixels, extra area is filled with the background color
|
||||
*/
|
||||
void size(const glm::vec2& size);
|
||||
|
||||
/*!
|
||||
* This GL parameter will be passed to `glTexParameter` for `GL_TEXTURE_MIN_FILTER` and `GL_TEXTURE_MAG_FILTER`.
|
||||
*
|
||||
* @param quality quality of texture scaling passed to `glTexParameter`
|
||||
*/
|
||||
void scaling_quality(GLint quality);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,11 @@ sb::Timer::operator bool() const
|
|||
return timing;
|
||||
}
|
||||
|
||||
sb::Timer::operator float() const
|
||||
{
|
||||
return _elapsed;
|
||||
}
|
||||
|
||||
void sb::Timer::toggle()
|
||||
{
|
||||
toggle(!*this);
|
||||
|
@ -50,11 +55,15 @@ void sb::Timer::update(float stamp)
|
|||
_stamp = stamp;
|
||||
if (previous_is_recorded)
|
||||
{
|
||||
frame_duration = stamp - stamp_previous;
|
||||
if (*this)
|
||||
{
|
||||
frame_duration = stamp - stamp_previous;
|
||||
_elapsed += frame();
|
||||
}
|
||||
else
|
||||
{
|
||||
frame_duration = 0.0f;
|
||||
}
|
||||
}
|
||||
stamp_previous = stamp;
|
||||
previous_is_recorded = true;
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace sb
|
||||
{
|
||||
/*!
|
||||
|
@ -38,6 +41,11 @@ namespace sb
|
|||
*/
|
||||
operator bool() const;
|
||||
|
||||
/*!
|
||||
* Convert a sb::Timer to a float by returning the elapsed time.
|
||||
*/
|
||||
operator float() const;
|
||||
|
||||
/*!
|
||||
* Toggle timing on/off
|
||||
*/
|
||||
|
@ -68,7 +76,6 @@ namespace sb
|
|||
*/
|
||||
float elapsed() const;
|
||||
|
||||
|
||||
/*!
|
||||
* @return length of the previous frame in seconds
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue