1// SuperTux
2// Copyright (C) 2016 Ingo Ruhnke <grumbel@gmail.com>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "video/ttf_surface.hpp"
18
19#include <SDL_ttf.h>
20
21#include <sstream>
22
23#include "util/log.hpp"
24#include "video/sdl_surface.hpp"
25#include "video/surface.hpp"
26#include "video/ttf_font.hpp"
27#include "video/ttf_surface_manager.hpp"
28#include "video/video_system.hpp"
29
30TTFSurfacePtr
31TTFSurface::create(const TTFFont& font, const std::string& text)
32{
33 SDLSurfacePtr text_surface(TTF_RenderUTF8_Blended(font.get_ttf_font(),
34 text.c_str(),
35 SDL_Color{255, 255, 255, 255}));
36 if (!text_surface)
37 {
38 log_warning << "Couldn't render text '" << text << "' :" << SDL_GetError();
39 return std::make_shared<TTFSurface>(SurfacePtr(), Vector());
40 }
41
42 // FIXME: handle shadow offset
43 int grow = std::max(font.get_border() * 2, font.get_shadow_size() * 2);
44
45 SDLSurfacePtr target = SDLSurface::create_rgba(text_surface->w + grow, text_surface->h + grow);
46
47#if !SDL_VERSION_ATLEAST(2,0,5)
48 // Perform blitting in ARGB8888, instead of RGBA8888, to avoid bug in older SDL2.
49 // https://bugzilla.libsdl.org/show_bug.cgi?id=3159
50 target.reset(SDL_ConvertSurfaceFormat(target.get(), SDL_PIXELFORMAT_ARGB8888, 0));
51#endif
52
53 { // shadow
54 SDL_SetSurfaceAlphaMod(text_surface.get(), 192);
55 SDL_SetSurfaceColorMod(text_surface.get(), 0, 0, 0);
56 SDL_SetSurfaceBlendMode(text_surface.get(), SDL_BLENDMODE_BLEND);
57
58 using P = std::tuple<int, int>;
59 const std::initializer_list<std::tuple<int, int> > positions[] = {
60 {},
61 {P{0, 0}},
62 {P{-1, 0}, P{1, 0}, P{0, -1}, P{0, 1}},
63 {P{-2, 0}, P{2, 0}, P{0, -2}, P{0, 2},
64 P{-1, -1}, P{1, -1}, P{-1, 1}, P{1, 1}}
65 };
66
67 int shadow_size = std::min(2, font.get_shadow_size());
68 for (const auto& p : positions[shadow_size])
69 {
70 SDL_Rect dstrect{std::get<0>(p) + 2, std::get<1>(p) + 2, text_surface->w, text_surface->h};
71 SDL_BlitSurface(text_surface.get(), nullptr,
72 target.get(), &dstrect);
73 }
74 }
75
76 { // outline
77 SDL_SetSurfaceAlphaMod(text_surface.get(), 255);
78 SDL_SetSurfaceColorMod(text_surface.get(), 0, 0, 0);
79 SDL_SetSurfaceBlendMode(text_surface.get(), SDL_BLENDMODE_BLEND);
80
81 using P = std::tuple<int, int>;
82 const std::initializer_list<std::tuple<int, int> > positions[] = {
83 {},
84 {P{-1, 0}, P{1, 0}, P{0, -1}, P{0, 1}},
85 {P{-2, 0}, P{2, 0}, P{0, -2}, P{0, 2},
86 P{-1, -1}, P{1, -1}, P{-1, 1}, P{1, 1}}
87 };
88
89 int border = std::min(2, font.get_border());
90 for (const auto& p : positions[border])
91 {
92 SDL_Rect dstrect{std::get<0>(p), std::get<1>(p), text_surface->w, text_surface->h};
93 SDL_BlitSurface(text_surface.get(), nullptr,
94 target.get(), &dstrect);
95 }
96 }
97
98 { // white core
99 SDL_SetSurfaceAlphaMod(text_surface.get(), 255);
100 SDL_SetSurfaceColorMod(text_surface.get(), 255, 255, 255);
101 SDL_SetSurfaceBlendMode(text_surface.get(), SDL_BLENDMODE_BLEND);
102
103 SDL_Rect dstrect{0, 0, text_surface->w, text_surface->h};
104
105 SDL_BlitSurface(text_surface.get(), nullptr, target.get(), &dstrect);
106 }
107
108#if !SDL_VERSION_ATLEAST(2,0,5)
109 target.reset(SDL_ConvertSurfaceFormat(target.get(), SDL_PIXELFORMAT_RGBA8888, 0));
110#endif
111
112 SurfacePtr result = Surface::from_texture(VideoSystem::current()->new_texture(*target));
113 return std::make_shared<TTFSurface>(result, Vector(0, 0));
114}
115
116TTFSurface::TTFSurface(const SurfacePtr& surface, const Vector& offset) :
117 m_surface(surface),
118 m_offset(offset)
119{
120}
121
122int
123TTFSurface::get_width() const
124{
125 if (m_surface) {
126 return m_surface->get_width();
127 } else {
128 return 0;
129 }
130}
131
132int
133TTFSurface::get_height() const
134{
135 if (m_surface) {
136 return m_surface->get_height();
137 } else {
138 return 0;
139 }
140}
141
142/* EOF */
143